Python для финансовых расчетов [Ив Хилпиш] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]
  [Оглавление]

O’REILLY

s

14

as
r*>

^'

•2

:>

QE

Sr

Я&3S
ftj

Ж

"0
5SJ

I
I

)

H
)

ИСКУССТВО РАБОТЫ С ФИНАНСОВЫМИ ДАННЫМИ

Ив Хилпиш

-

2 Е ИЗДАНИЕ

Python
для финансовых
расчетов

SECOND EDITION

Python
for Finance
Mastering Data-Driven Finance

Yves Hilpisch

Beijing • Boston • Farnham • Sebastopol « Tokyo

O’REILLY *

2-Е ИЗДАНИЕ

Python
для финансовых
расчетов

Искусство работы с финансовыми данными

Ив Хилпиш

КиТв
Комп'ютерне видавництво
"Д 1 АЛЕКТИКА"
2021

УДК 336(075.8)
А95
Перевод с английского И .В. Василенко
Под редакцией В.Р. Гинзбурга

Хилпиш, Ив.

Х45

Python для финансовых расчетов, 2-е изд./ Ив Хилпиш; пер.
с англ. И.В. Василенко. Киев. : “ Диалектика”, 2021. 800 с. : ил.
Парал. тит. англ.
ISBN 978-617-7874- 29- 3 (укр.)
ISBN 978-1-492 -02433-0 ( англ.)







В новом издании книги разработчики и финансовые аналитики узнают, как
применять различные инструменты Python для создания финансовых прило жений и систем алгоритмической торговли. Все примеры книги написаны на
Python 3 и доступны в виде интерактивных блокнотов Jupyter. Готовые программные решения помогут понять, как экосистема Python формирует техноло гический фундамент для финансовой индустрии.
УДК 336(075.8)

Все права защищены.
Все названия программных продуктов являются зарегистрированными торговыми марками соответствующих фирм.

Никакая часть настоящего издания ни в каких целях не может быть воспроизведена в
какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или
механические, включая фотокопирование и запись на магнитный носитель, если на это нет
письменного разрешения издательства O' Reilly & Associates.

Copyright © 2021 by Dialektika Computer Publishing.
Authorized Russian translation of the English edition of Python for Finance, 2nd Edition ( ISBN
978 - 1 - 492- 02433-0 ) © 2019 Yves Hilpisch.
This translation is published and sold by permission of O'Reilly Media, Inc., which owns or
controls all rights to sell the same.
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any
means, electronic or mechanical, including photocopying, recording, or by any information storage
or retrieval system, without the prior written permission of the copyright owner and the Publisher.

ISBN 978-617-7874- 29-3 ( укр.)
ISBN 978- 1- 492-02433-0 ( англ.)

© “ Диалектика ”, перевод, 2021
© 2019 Yves Hilpisch

Оглавление
Введение

19

.

Часть I Python и финансовые вычисления
Глава 1. Python как инструмент финансовых расчетов
Глава 2. Инфраструктура Python

27
63

.

Часть II Основы Python
Глава 3. Типы данных и структуры Python
Глава 4 Работа с массивами NumPy

121

Глава 6. Объектно-ориентированное программирование

153
191

.
Глава 5. Анализ данных с помощью библиотеки pandas

93

.

Часть III Обработка и анализ финансовых данных

.

Глава 7 Визуализация данных

215

Глава 8 Финансовые временные ряды
Глава 9 Операции ввода -вывода
Глава 10. Производительность Python
Глава 11 Математические инструменты
Глава 12 Стохастические методы
Глава 13 Статистический анализ

255

.
.

283
333

.
.
.
Часть IV. Алгоритмическая торговля

465

Глава 14. Торговая платформа FXCM
Глава 15 Торговые стратегии
Глава 16 Автоматизированная торговля

545
561
607

Глава 17. Принципы оценки опционов
Глава 18. Финансовое моделирование
Глава 19 Оценка деривативов
Глава 20. Оценка портфеля
Глава 21 Оценка на основе рыночных данных

649

Приложение А. Обработка значений даты и времени
Приложение Б Класс опционов в модели Блэка — Шоулза — Мертона
Предметный указатель

765

.
.
Часть V. Анализ деривативов
.

.
Часть VI. Приложения
.

373
409

665
691

717
739

781
787

Содержание
Об авторе
Об изображении на обложке

Введение
Соглашения, принятые в книге
Файлы примеров к книге
Ждем ваших отзывов!

17
17

19
22
23
24

Часть I . Python и финансовые вычисления
Глава 1 . Python как инструмент финансовых расчетов
Язык программирования Python
Краткая история Python
Экосистема Python
Круг пользователей Python
Стек научных пакетов
Технологии в финансовой отрасли
Инвестиции в технологии
Технологии как движущая сила
Технологии и кадры решают все
В погоне за скоростью, производительностью и объемами данных
Анализ в реальном времени
Python для финансовых расчетов
Синтаксис Python, применяемый в финансовых вычислениях
Эффективность и производительность кода Python
От прототипа к готовому приложению
Финансовые расчеты на основе данных и искусственного интеллекта
Финансовые системы, управляемые данными
Финансовые системы на основе искусственного интеллекта
Резюме
Дополнительные ресурсы

Глава 2. Инфраструктура Python
conda как менеджер пакетов
Установка Miniconda
Выполнение основных команд в менеджере conda

27
27
30
31
33
33
35
35
36
37
38
39
40
41
45
51
52
52
57
60
61

63
65
65
67

conda как менеджер виртуального окружения
Контейнеры Docker
Контейнеры и образы
Создание образа Docker с Ubuntu и Python
Облачные экземпляры
Открытый и закрытый ключи RSA
Конфигурационный файл Jupyter Notebook
Сценарий установки Python и Jupyter Notebook
Сценарий оркестровки для процесса установки дроплета

Резюме
Дополнительные ресурсы

72
76
76
77
82
83
84
86
87
89
90

Часть II. Основы Python
Глава 3. Типы данных и структуры Python

93

Основные типы данных
Целые числа
Числа с плавающей точкой

Множества
Резюме
Дополнительные ресурсы

94
94
95
98
102
104
107
109
109
110
112
114
116
117
119
119

Глава 4. Работа с массивами NumPy

121

Булевы значения
Строки
Пример: вывод и замена строк
Пример: регулярные выражения
Основные структуры данных
Кортежи
Списки

Пример: управляющие конструкции
Пример: функциональное программирование
Словари

Массивы данных

122
122
124
127
127
130

Преобразование списка в массив
Класс array

Обычные массивы NumPy
Основные операции
Многомерные массивы
Метаинформация

134
Содержание

7

Изменение формы и размера массива
Булевы массивы
Скорость выполнения операций
Структурированные массивы NumPy
Векторизация кода
Основные способы векторизации

Эффективное использование памяти
Резюме
Дополнительные ресурсы

.

Глава 5 Анализ данных с помощью библиотеки pandas
Класс DataFrame
Знакомство с классом DataFrame
Расширенные возможности класса DataFrame
Основные аналитические возможности
Основные инструменты визуализации
Класс Series
Группирование данных
Сложные операции извлечения данных
Конкатенация, соединение и слияние данных
Конкатенация
Соединение
Слияние
Производительность вычислений
Резюме
Дополнительные ресурсы

Глава 6. Объектно-ориентированное программирование

Обзор объектов Python
int
list
ndarray
DataFrame
Основные операции с классами Python
Модель данных Python
Код класса Vector
Резюме
Дополнительные ресурсы

8

I

Содержание

135
139
141
143
145
145
149
151
152

153
154
154
159
164
168
171

172
175
179
179
181
183
186
189
189

191

195
195
196
197
199
201
206
211
212
212

Часть III. Обработка и анализ финансовых данных
Глава 7. Визуализация данных

215

Статические двухмерные графики
Одномерные наборы данных
Двухмерные наборы данных
Другие типы диаграмм
Статические трехмерные диаграммы
Интерактивные двухмерные диаграммы
Базовые графики
Финансовые диаграммы
Резюме
Дополнительные ресурсы

216
217
223
231
239
243
243
248
252
253

Глава 8. Финансовые временные ряды

255

Финансовые данные
Импорт данных
Статистическая сводка
Изменения во времени
Прореживание данных
Скользящая статистика
Общие сведения
Пример технического анализа

Высокочастотные данные
Резюме
Дополнительные ресурсы

256
256
260
263
266
268
269
271
274
274
276
277
278
279
282
282

Глава 9. Операции ввода-вывода

283

Корреляционный анализ
Исходные данные

Логарифмическая доходность
Регрессионный анализ по методу наименьших квадратов
Корреляция

Базовые операции ввода-вывода в Python
Запись объектов на диск
Чтение и запись текстовых файлов
Работа с реляционными базами данных
Считывание и запись массивов NumPy
Ввод и вывод данных с помощью библиотеки pandas

284
285
288
292
295
299

Содержание

9

Работа с реляционными базами данных
Импорт данных из реляционных баз данных
Работа с CSV-файлами
Работа с файлами Excel
Ввод и вывод данных с помощью PyTables
Работа с таблицами
Работа со сжатыми таблицами
Работа с массивами
Вычисления в условиях нехватки памяти
Ввод и вывод данных с помощью TsTables
Исходные данные
Хранение данных
Извлечение данных
Резюме
Дополнительные ресурсы

300
302
305
306
308
308
317
319
321
324
325
326
328
330
331

Глава 10. Производительность Python

333

Циклы

Python
NumPy
Numba
Cython
Алгоритмы
Простые числа
Числа Фибоначчи
Число п
Биномиальные деревья
Python

NumPy
Numba
Cython
Метод Монте-Карло

Python
NumPy
Numba
Cython
Параллельные вычисления
Рекурсивный алгоритм библиотеки pandas
Python
Numba
10

Содержание

334
335

336
337
338
340
340
345
349
353
354
355
357
358
359
361
362
363
364
365
366
367
369

Cython

370
371
372

Резюме
Дополнительные ресурсы

.

Глава 11 Математические инструменты

373

Аппроксимация
Регрессия
Интерполяция
Выпуклое программирование
Глобальная оптимизация

374
375
386
391
392
394
395
398
400
400
401
401
404
404
406
407
408

Локальная оптимизация
Условная оптимизация

Интегрирование
Численное интегрирование
Интегрирование методами моделирования
Символьные вычисления
Общие сведения
Решение уравнений

Интегрирование

Дифференцирование
Резюме
Дополнительные ресурсы

.

Глава 12 Стохастические методы

409

Случайные числа
Моделирование
Случайные переменные
Случайные процессы
Уменьшение дисперсии

410
417
417
421
438
441
442
448
451
451
456
460
463
464

Оценка опционов
Европейские опционы
Американские опционы
Оценка рисков
Стоимость под риском

Поправка на кредитный риск
Общий сценарий Python
Резюме
Дополнительные ресурсы

Содержание

11

Глава 13. Статистический анализ
Нормальное распределение
Эталонный портфель
Существующие исторические данные
Оптимизация портфеля
Данные

Теоретическое обоснование
Оптимальный портфель
Граница эффективности
Линия рынка капиталов
Байесовская статистика
Формула Байеса
Байесовская регрессия
Два финансовых инструмента
Обновление оценочных значений со временем
Машинное обучение
Обучение без учителя
Обучение с учителем
Резюме
Дополнительные ресурсы

465
466
467
479
486
486
488
493
496
498
502
502
503
508
513
518
519
522
540
541

Часть IV. Алгоритмическая торговля
Глава 14. Торговая платформа FXCM
Настройка программного интерфейса FXCM
Получение данных
Получение тиковых данных
Получение свечных данных
Работа с программным интерфейсом FXCM
Получение исторических данных
Получение потоковых данных
Размещение заявок
Учетные данные
Резюме
Дополнительные ресурсы

Глава 15. Торговые стратегии
Простое скользящее среднее
Импорт данных

12

Содержание

545
546
547
548

550
553
553
556
557
559
560
560
561

562
563

Торговая стратегия
Векторизованное тестирование на исторических данных
Оптимизация
Гипотеза случайного блуждания
Линейная регрессия по методу наименьших квадратов
Данные
Регрессия
Кластеризация
Частотный подход

Классификация
Два бинарных признака
Пять бинарных признаков
Пять дискретизированных признаков
Последовательное разделение данных на обучающий
и тестовый наборы
Рандомизированное разделение данных на обучающий
и тестовый наборы
Глубокие нейронные сети
DNN и библиотека Scikit -learn
DNN и библиотека TensorFlow

564
566
569
571
575
575
578
581
583
586
586
588
590
592
594
596
596
599
604
604

Резюме
Дополнительные ресурсы

Глава 16. Автоматизированная торговля
Управление капиталом
Критерий Келли в биномиальной модели
Критерий Келли в биржевой торговле
Торговая стратегия, основанная на машинном обучении
Векторизованное тестирование на исторических данных
Оптимальный леверидж
Анализ рисков
Сохранение объекта модели
Веб -алгоритм
Инфраструктура и развертывание
Протоколирование и мониторинг
Резюме
Сценарии Python
Автоматизированная торговая стратегия
Мониторинг стратегии
Дополнительные ресурсы
Содержание

607
608
608
614

619
620
626
628
633
633
636
638
640
641
641
644
645
13

Насть V. Анализ деривативов
Глава 17. Принципы оценки опционов
Фундаментальная теорема ценообразования финансовых активов
Простой пример
Общая модель
Риск - нейтральное дисконтирование
Моделирование и обработка дат
Постоянная краткосрочная ставка
Рыночная среда
Резюме
Дополнительные ресурсы

Глава 18. Финансовое моделирование
Генерирование случайных чисел
Общий класс моделирования
Геометрическое броуновское движение
Класс моделирования
Пример использования
Прыжковая диффузия
Класс моделирования
Пример использования
Диффузия по закону квадратного корня
Класс моделирования
Пример использования
Резюме

Дополнительные ресурсы

Глава 19. Оценка деривативов
Общий класс оценки деривативов
Европейский опцион
Класс оценки
Пример использования
Американский опцион
Метод наименьших квадратов Монте-Карло
Класс оценки
Пример использования
Резюме
Дополнительные ресурсы

14

Содержание

649
650
650
651
653
653
656
658
662
663

665
666
668
673
673
676
679
679
682
684
685
687
689
690

691
692
696
697
699
705
705
707
710
713
715

Глава 20. Оценка портфеля

717

Деривативные позиции

718
718
721
722
723
728
736
738

Класс деривативной позиции
Пример использования

Портфели деривативов
Класс портфеля
Пример использования
Резюме
Дополнительные ресурсы
Глава 21. Оценка на основе рыночных данных

739

Данные опционов

740
743
743
745
748

Калибровка модели
Релевантные рыночные данные
Моделирование опционов
Процедура калибровки

Оценка портфеля

755
755
756

Моделирование опционных позиций
Портфель опционов
Код Python
Резюме
Дополнительные ресурсы

758
760
761

Часть VI. Приложения
Приложение А. Обработка значений даты и времени

765

Python

765
771
775

NumPy
pandas

Приложение Б. Класс опционов в модели Блэка — Шоулза — Мертона

781

Определение класса
Пример использования

781
783

Предметный указатель

787

Содержание

15

Об авторе



Ив Хилпиш основатель проекта The Python Quants ( https : / / home . tpq .
io / ) , ориентированного на применение программных продуктов с открытым
исходным кодом в финансовом анализе, искусственном интеллекте и алгоритмической торговле. Он также возглавляет компанию The AI Machine ( http : / /
aimachine . io / ) , которая занимается разработкой стандартизированных си стем алгоритмической торговли на базе искусственного интеллекта.
Помимо этого Ив Хилпиш читает курс финансовой инженерии в рамках
проекта CQF ( www . cqf . com ) и руководит первой обучающей онлайн - програм мой, нацеленной на получение “ университетского сертификата по алгоритми ческой торговле на Python” ( http : / / certificate . tpq . io / ) .
Ив Хилпиш написал библиотеку DX Analytics ( http : / / dx - analytics .
com / ), представляющую собой пакет эффективных инструментов финансового анализа. Он проводит конференции и семинары, посвященные искусственному интеллекту, финансовому анализу и алгоритмической торговле, по

всему миру.

Об изображении на обложке



это гаитянский щелезуб
Животное, изображенное на обложке книги,
( лат. Solenodon paradoxus), исчезающий вид млекопитающих, который в жи вой природе встречается исключительно на острове Гаити. Его ареал обита ния охватывает в основном Доминиканскую Республику и в незначительной
степени Республику Гаити.
Питается гаитянский щелезуб преимущественно членистоногими, червя ми, улитками и пресмыкающимися, но в его рацион также входят коренья,
фрукты и листья. Вес взрослой особи составляет около килограмма, длина
тела 30-35 см, длина хвоста 20-25 см. Внешне напоминает крупную зем леройку. Все его тело, за исключением лап, хвоста и мордочки, покрыто рыжей
шерстью, которая заметно светлее на животе, чем на спине.
Щелезуб ведет скрытный, преимущественно ночной образ жизни. Днем
спит в естественных укрытиях или в норах. Двигается неуклюже, но бегает довольно быстро. Как и любые другие ночные животные обладает прекрасным
слухом, обонянием и осязанием. У щелезуба очень характерный мускусный





запах.



Гаитянские щелезубы ядовиты: токсичная слюна, которая парализует жертву, выделяется подчелюстной слюнной железой и впрыскивается через глубокую бороздку второго нижнего резца. Примечательно, что они не имеют
иммунитета к собственному яду и могут погибнуть от укусов, полученных во
время драк между собой. Образовав устойчивые социальные отношения, щелезубы подолгу проживают небольшими семьями на одном месте.
Многие из животных, изображаемых на обложках книг издательства
O’ Reilly, находятся под угрозой вымирания, и все они представляют ценность
для нашего мира. Чтобы узнать о том, каким может быть ваш личный вклад в
их спасение, посетите сайт animals . oreilly . com.
Изображение на обложке взято из книги Illustrated Natural History Джона
Георга Вуда.

Введение
В наши дни Python является одним из ключевых инструментов разработки
стратегических технологий в финансовой сфере. Когда я приступил к напи санию первого издания книги в 2013 году, мне приходилось проводить множество бесед и презентаций, бесконечно убеждая скептиков в конкурентных
преимуществах Python как языка разработки финансовых приложений по
сравнению с другими языками программирования и платформами. Спустя
пять лет уже ни у кого не осталось никаких сомнений: финансовые учреждения по всему миру активно применяют Python и его разветвленную экосистему пакетов анализа данных, визуализации и машинного обучения.
Помимо финансовой сферы Python зачастую является языком выбора в
курсах изучения программирования. Причина заключается не только в понятном синтаксисе и поддержке множества парадигм, но и в наличии продвинутых средств разработки приложений в таких областях, как искусствен ный интеллект, машинное обучение и глубокое обучение. Самые популярные
пакеты и библиотеки для этих областей либо написаны непосредственно на
Python ( как, например, Scikit -learn ), либо содержат оболочки, написанные
на Python ( например, TensorFlow).
Финансовая отрасль сама по себе вступает в новую эпоху, определяемую
двумя движущими факторами. В первую очередь это появление программ ного доступа практически ко всем доступным финансовым данным. Причем
такой доступ возможен в режиме реального времени, что позволяет строить
финансовые системы, управляемые данными. В былые времена большинство
торговых и инвестиционных решений принималось трейдерами или финан совыми менеджерами на основании информации, полученной через СМИ или
личные контакты. С появлением компьютеров стало возможным просматри вать текущую биржевую информацию на экране монитора, но в современном
мире ежеминутно генерируется такой объем финансовых данных, что чело веку за ним просто не поспеть. Справиться с обработкой нескончаемого потока финансовой информации способны только компьютеры. Как следствие,
большинство операций по торговле финансовыми активами управляется программами, а не трейдерами.

Вторым фактором стала всевозрастающая роль искусственного интеллекта в финансовых расчетах. Все больше финансовых учреждений применяет
алгоритмы машинного и глубокого обучения в операционной деятельности и
в системах принятия инвестиционных решений. Первая специализированная
книга, посвященная применению машинного обучения в финансах, вышла в
начале 2018 года, и за ней последовала лавина других подобных книг. Все это
ведет к появлению финансовых систем на основе искусственного интеллекта , в которых гибкие, параметризуемые алгоритмы машинного и глубокого
обучения замещают традиционную финансовую теорию, эффективную в прошлом, но не способную справиться с вызовами новой эпохи.
Python со своей экосистемой как нельзя лучше подходит на роль ведущего
языка программирования для разработки современных финансовых решений. Несмотря на то что в книге будут рассматриваться базовые алгоритмы
машинного обучения ( и нейронных сетей ), основной акцент сделан на ин струментах обработки и анализа данных. Чтобы в полном объеме охватить
тему применения искусственного интеллекта в финансах, пришлось бы напи сать отдельную книгу. В то же время методы искусственного интеллекта требуют наличия столь огромных объемов данных, что в первую очередь следует
научиться управлять данными.
Второе издание книги было существенно переработано. В частности, добавилась часть IV, посвященная алгоритмической торговле, которая набирает популярность в финансовой индустрии. Была также расширена часть II,
посвященная описанию основных инструментов Python, которые будут задействованы в последующих частях. Наряду с этим из нового издания было
удалено несколько глав, посвященных веб- пакетам типа Flask, которые доста точно подробно описаны в специализированной литературе.
Во втором издании рассмотрено большое число тем, связанных с финан совыми вычислениями. Основное внимание уделяется инструментам Python,
применяемым для работы с финансовыми данными. Как и в первом издании,
подход носит прикладной характер, поскольку предпочтение отдается программной реализации и представлению результатов, а не теоретическим выкладкам. При этом мы будем стараться увидеть общую картину, а не концен трироваться на параметрах отдельных классов, методов или функций.
Следует подчеркнуть, что книга не является ни введением в программирование на Python, ни учебником по финансам в целом. Каждая из этих тем
подробно рассматривается во множестве великолепных руководств. Книга
находится как бы на стыке двух областей, поэтому от читателя предполагается
наличие базовых знаний по программированию ( не обязательно на Python ) и

20

Введение

финансам. Книга поможет узнать, как применять Python и его библиотеки для
решения финансовых задач.
Блокноты Jupyter и примеры программ, используемые в книге, доступны на
авторском сайте Quant Platform (http: / / py 4fi . pqp . io ) , на котором можно
зарегистрироваться бесплатно.
Компания The Python Quants предлагает множество ресурсов, посвящен ных применению Python в финансовом анализе, искусственном интеллекте и
алгоритмической торговле. Для начала посетите следующие сайты:






сайт компании The Python Quants (https : / / tpq . io / );



сайт программы сертификации (https : / / certificate . tpq io / ).

сайт автора книги (http : / / hilpisch . con / );

.

.

сайт, посвященный книгам автора ( https : / / books tpq io / );

сайт, посвященный авторским онлайн - курсам (https : / / training . tpq .
to / );

.

Среди всех проектов, реализованных автором за последние несколько лет,
самое большое достижение это программа сертификации специалистов по
алгоритмической торговле на Python. Участникам программы предлагается
более 150 лекционных часов, 1200 страниц документации, 5000 строк кода
Python и 60 блокнотов Jupyter. Набор в программу ( к слову, постоянно обновляемую и дополняемую новыми курсами ) проводится несколько раз в год.
Это первая онлайн - программа такого рода, в которой выпускникам выдается
официальный университетский диплом в сотрудничестве с Саарским университетом прикладных наук (http : / / htwsaar . de / ).
Помимо этого автор запустил платформу The AI Machine ( https : / /
aimachine . io / ), предназначенную для внедрения автоматизированных систем алгоритмической торговли. В рамках данного проекта мы стремимся реализовать все то, чему обучали студентов за последние годы. В наши дни бла годаря Python и технологиям искусственного интеллекта подобные проекты
становятся возможными даже для таких небольших команд, как наша.
Введение к первому изданию книги заканчивалось такими словами.



Мне необычайно приятно осознавать, что Python зарекомендовал себя пере довой технологией в финансовой сфере. Убежден , в будущем он станет играть
еще более важную роль в таких областях, как анализ рисков и высокопроиз водительные вычисления. Надеюсь, книга поможет профессиональным разра ботчикам, ученым и студентам применять Python для решения самых сложных
задач.

Введение

21

В далеком 2014 году сложно было представить, насколько широко Python
будет применяться в финансовых расчетах. Спустя пять лет стоит признать,
что действительность превзошла самые смелые ожидания. Хочется верить,
что первое издание книги внесло свою лепту в продвижение Python. В любом
случае стоит сказать огромное спасибо всем разработчикам программного
обеспечения с открытым исходным кодом за их неутомимый труд, благодаря
которому сказка стала былью.

Соглашения, принятые в книге
В этой книге приняты следующие условные обозначения.

Курсив
Служит для выделения ключевых терминов, которые следует знать.
Моноширинный шрифт

Используется для оформления листингов, а также для выделения имен
файлов, пакетов и программных элементов, в частности операторов,
переменных, ключевых слов и т.п. Этим же шрифтом выделяются URLадреса.
Моноширинный курсив

Применяется для выделения программных элементов, которые должны
вводиться пользователем или заменяться значениями по контексту.

ш

Этой пиктограммой помечаются советы или рекомендации.

Этой пиктограммой помечаются примечания к основному тексту.

Этой пиктограммой помечаются предупреждения, на которые следует обратить внимание.

22

Введение

Файлы примеров к книге
Все примеры программ, используемые в книге (в частности, блокноты
Jupyter и исходные коды сценариев и программных модулей Python ) , доступны на сайте книги:
http : / / py 4fi . pqp . io

Пройдя бесплатную регистрацию, вы получите доступ к программной среде, в которой можно как скачать все примеры для самостоятельного развертывания, так и запустить каждый блокнот Jupyter прямо на сайте. Все блокноты глав доступны в разделе Notebooks 1 . Доступ к файловому хранилищу
можно получить с помощью команды Tools File Manager. Инструкции по
работе с сайтом содержатся в видеоролике, который вызывается по команде
Help 1 Platform .

^

^

1

В ряде случаев примеры, приводимые в книге, незначительно отличаются от кода , содержащегося в блокнотах. Изменения в основном связаны с необходимостью получения корректных
версий рисунков. При возникновении спорных ситуаций сверяйтесь с кодом, приведенным
в книге Примен. ред.



Введение

23

Ждем ваших отзывов!
.

Вы, читатель этой книги, и есть главный ее критик Мы ценим ваше мнение
и хотим знать, что было сделано нами правильно, что можно было сделать
лучше и что еще вы хотели бы увидеть изданным нами. Нам интересны любые
ваши замечания в наш адрес
Мы ждем ваших комментариев и надеемся на них. Вы можете прислать нам
электронное письмо либо просто посетить наш сайт и оставить свои замечания там Одним словом, любым удобным для вас способом дайте нам знать,
нравится ли вам эта книга, а также выскажите свое мнение о том, как сделать
наши книги более интересными для вас
Отправляя письмо или сообщение, не забудьте указать название книги и ее
авторов, а также свой обратный адрес Мы внимательно ознакомимся с вашим
мнением и обязательно учтем его при отборе и подготовке к изданию новых

.

.

.

.

книг.

Наши электронные адреса:

.

E-mail:

info.dialektika@gmail com

WWW:

http: / / www williamspublishing . com

24

.

Введение

ЧАСТЬ I

Python и финансовые вычисления

Это вводная часть, включающая всего две главы.

• Глава 1 посвящена знакомству с языком программирования Python. Вы
узнаете, почему именно он выбран основным инструментом разработки
приложений, предназначенных для финансовых расчетов и анализа фи нансовых данных.

• Глава 2 посвящена инфраструктуре Python. Вы узнаете о том, как начать
работу в среде Python для выполнения финансовых расчетов в интерактивном режиме.

ГЛАВА 1

Python как инструмент финансовых расчетов
Банки



самые высокотехнологичные предприятия.
Хуго Банцигер

Язык программирования Python
Python — это универсальный язык программирования высокого уровня,
который широко применяется в самых разных областях. На официальном
сайте Python ( https : / / www . python . org / doc / essays / blurb) он характеризуется
следующим образом .



это интерпретируемый, объектно-ориентированный, высокоуров Python
невый язык программирования с динамической семантикой . Высокоуровне вые структуры данных в сочетании с динамической типизацией и динамическим связыванием делают его удобным инструментом быстрой разработки
приложений , а также привлекательным средством написания сценариев и
языком интеграции существующих компонентов. Простой в изучении и по нятный синтаксис языка ориентирован на удобство чтения кода, что позво ляет снизить затраты на сопровождение программ. В Python поддерживаются
модули и пакеты, что облегчает повторное использования кода и способствует повышению модульности программ. Интерпретатор и богатая стандарт ная библиотека языка свободно доступны как в виде исходных кодов, так и в
бинарном виде для большинства компьютерных платформ и могут свободно

распространяться.

Приведенное описание как нельзя лучше показывает, почему Python стал
одним из самых популярных языков программирования . В наши дни им поль зуются как начинающие программисты , так и профессиональные разработ чики. Он применяется везде: в школах, университетах , крупных компаниях и
финансовых учреждениях, а также на веб -сайтах и в научной среде.

Среди преимуществ Python можно выделить следующее.
Открытый исходный код
Большинство программных инструментов и библиотек Python находится в свободном доступе и распространяется на условиях открытой ли цензии.

Интерпретируемость
Эталонной реализацией считается интерпретатор CPython, преобразующий код Python в байтовый код, непосредственно выполняемый ком пьютером.
Мулътипарадигменностъ
Python поддерживает самые разные стили программирования, в частности объектно-ориентированный, императивный, функциональный и
процедурный.

Универсальность
Python подходит для написания как простых интерактивных приложений, так и сложных многофункциональных программ. При этом он
прекрасно справляется с выполнением как низкоуровневых системных
операций, так и высокоуровневых аналитических задач.
Многоплатформенность
Python доступен для всех популярных операционных систем, таких как
Windows, Linux и macOS. Он позволяет писать как настольные, так и
веб- приложения, которые можно запускать как в огромных серверных
кластерах, так и на небольших устройствах типа Raspberry Pi.
Динамическая типизация
В Python типы данных определяются непосредственно на этапе выпол нения, а не объявляются статически, как в большинстве компилируемых языков программирования.

Поддержка отступов в коде
В отличие от других языков программирования в Python структура программы ( группировка операторов ) задается отступами, что позволяет
не использовать фигурные скобки, точки с запятой и другие разделители.

Сбор мусора
В Python реализован автоматический механизм сбора мусора, что избавляет программиста от необходимости заниматься управлением памятью.

28

Глава 1

Точнее всего философия языка Python изложена в следующих 20 положе ниях, известных как “ The Zen of Python” Для ознакомления с ними достаточно
выполнить команду import this в любой интерактивной оболочке Python.
In [ 1] : import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly .
Explicit is better than implicit
Simple is better than complex .
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense
Readability counts
Special cases aren ' t special enough to break the rules
Although practicality beats purity
Errors should never pass silently
Unless explicitly silenced
In the face of ambiguity , refuse the temptation to guess
There should be one - - and preferably only one - - obvious
way to do it .
Although that way may not be obvious at first unless
you ' re Dutch
Now is better than never
Although never is often better than * right * now
If the implementation is hard to explain, it ' s a bad idea .
If the implementation is easy to explain, it may be
a good idea
Namespaces are one honking great idea - - let ' s do more
of those !

.

.

.

.

.

.

.

.

.

.

.

.

В переводе на русский язык они звучат так.

• Красивое лучше, чем уродливое.



Явное лучше, чем неявное.

• Простое лучше, чем сложное.




Сложное лучше, чем запутанное.

Плоское лучше, чем вложенное.

• Разреженное лучше, чем плотное.
• Читаемость имеет значение.
• Особые случаи не настолько особые, чтобы нарушать правила.
Python как инструмент финансовых расчетов

29

• При этом практичность важнее безупречности.
• Ошибки никогда не должны замалчиваться.
• Если не замалчиваются явно.
• Встретив двусмысленность, отбрось искушение угадать.
• Должен существовать один — и желательно только один —

очевидный

способ сделать это.

• Хотя он поначалу может быть и не очевиден, если вы не голландец.
• Сейчас лучше, чем никогда.
• Хотя никогда зачастую лучше, чем прямо сейчас.
• Если реализацию сложно объяснить — идея плоха.
• Если реализацию легко объяснить — идея, возможно, хороша.
• Пространства имен — отличная вещь! Давайте будем делать их больше!

Краткая история Python
Некоторые до сих пор считают Python новым языком, хотя он существует уже достаточно давно. Его разработка началась в 80-х годах XX века программистом из Нидерландов Гвидо ван Россумом. Он длительное время участвовал в развитии языка на правах великодушного пожизненного диктатора
( Benevolent Dictator For Life
почетный титул, который ему при BDFL)
своило сообщество Python. Только в июле 2018 года, после нескольких деся тилетий активной работы, Гвидо ван Россум принял решение оставить свой
почетный пост BDFL и стать рядовым разработчиком.
Ниже перечислены основные вехи развития Python:












30



Python 0.9.0, 1991 год ( первый релиз );

Python 1.0, 1994 год;
Python 2.0, 2000 год;
Python 2.6, 2008 год;
Python 3.0, 2008 год;
Python 3.1, 2009 год;
Python 2.7, 2010 год;
Python 3.2, 2011 год;

Python 3.3, 2012 год;
Глава 1






Python 3.4, 2014 год;



Python 3.8, 2019 год;

Python 3.5, 2015 год;
Python 3.6, 2016 год;
Python 3.7, 2018 год;

Новичков часто сбивает с толку тот факт, что начиная с 2008 года актуальными остаются сразу две версии Python, которые существуют параллельно. Это связано с наличием огромного количества программ, написанных на
Python 2.6 / 2.7, которые продолжают активно использоваться. В то же время
новые проекты пишутся на Python 3.6-3.8. Примеры, приводимые в книге,
ориентированы на версию 3.7.

Экосистема Python



Python это не только язык программирования, но и целая экосистема,
включающая большое количество библиотек и различных программных ин струментов. Их необходимо импортировать по мере необходимости ( как, на пример, библиотеку построения диаграмм ) или запускать в виде отдельного
системного процесса ( как, например, интерактивную среду разработки ). Операция импорта делает пакет доступным в текущем пространстве имен и в текущем процессе интерпретатора.
В базовый дистрибутив Python уже входит большой набор пакетов и модулей, расширяющих функциональность интерпретатора . Этот набор называет ся стандартная библиотека Python ( https : / / docs . python . Org / 3 / library /
index . html ) . Например, общие математические вычисления не требуют под ключения каких-либо модулей, тогда как специализированные математи ческие функции импортируются через модуль math .
In [ 2 ] : 100 * 2.5 + 50
0ut[ 2 ] : 300.0

In [ 3 ] : log ( l )

О

NameError
Traceback ( most recent call last )
< ipython - input - 3 - 74 f 22a 2 fd 43 b > in < module >
- - - - > 1 log ( l ) О

NameError : name ' l o g ' is not defined

Python как инструмент финансовых расчетов

31

In [ 4 ] : import math ©
In [ 5 ] : math.log(l)
Out [ 5 ]: 0.0

©

О Без импорта соответствующего модуля эта строка вызовет ошибку.

© После импорта модуля math математические вычисления выполняются
корректно.



Модуль math это стандартный компонент, доступный в любом дистри бутиве Python, но есть множество других пакетов, которые устанавливаются
опционально, после чего применяются так же, как и стандартные модули. Та кие пакеты доступны на специализированных сайтах в Интернете. Для работы с ними рекомендуется использовать менеджер пакетов (он будет рассмотрен в главе 2 ), который гарантирует совместимость всех библиотек.
Приведенные примеры ориентированы на две самые популярные среды:
IPython ( http : / / ipython . org / ) и Jupyter ( https : / / jupyter . org / ) . Проект
IPython задумывался как улучшенная интерактивная оболочка Python, но
со временем превратился в интегрированную среду разработки ( Integrated
Development Environment IDE) с поддержкой таких средств, как профилирование и отладка. Расширенные инструменты редактирования кода доступ ны через внешние редакторы типа Vim ( http : / / vim . org ) , которые можно ин тегрировать в IPython.
IPython существенно расширяет возможности стандартной интерактивной оболочки. Среди ключевых дополнений
улучшенные средства работы
с историей командной строки и поддержка инспектирования объектов. Например, чтобы просмотреть справку по функции ( docstring ) , достаточно
предварить или завершить ее имя знаком вопроса ( добавление двух знаков
вопроса позволит получить расширенную справку).
Первоначально IPython поставлялся в двух вариантах: как оболочка и как
браузерная версия ( Notebook ) . Последняя приобрела настолько большую популярность, что была выделена в отдельный многоязыковой проект, получивший название Jupyter. Как следствие, среда Jupyter Notebook наследует все самое лучшее от IPython, дополняя ее множеством расширенных инструментов,
особенно в части визуализации данных.





32

Глава 1

Круг пользователей Python
Неправильно полагать, будто Python интересен только профессиональным
разработчикам. Это универсальный язык, применяемый в самых разных об ластях, в том числе для научных расчетов.
Профессиональные разработчики находят в Python все то, что необходи мо для создания крупномасштабных приложений. В Python поддерживаются
практически все основные парадигмы программирования и имеются средства
для решения любых задач. Профессионалы часто пишут собственные библиотеки и классы и по максимуму задействуют весь стек научных пакетов.
Разработчики специализированных и научных приложений, как правило,
активно пользуются конкретными пакетами и библиотеками, адаптируют
экосистему под собственные задачи и постоянно дорабатывают и оптимизи руют создаваемые программы. Они подолгу экспериментируют в рамках ин терактивных сеансов, исследуя и визуализируя имеющиеся наборы данных.
Рядовые программисты обращаются к Python для решения конкретных
задач, когда заранее известно, что имеются готовые наработки. Например,
программист может посетить страницу с галереей визуализаций библиотеки
matplotlib, скопировать оттуда готовый фрагмент кода и вставить его в соб ственный проект, адаптировав к условиям задачи. Это сильно ускоряет процесс разработки.
Есть еще одна многочисленная категория пользователей Python: начинающие программисты. На сегодняшний день Python стал одним из самых популярных языков, с которого начинают изучать программирование в университетах, колледжах и даже школах. Причиной служит простой и понятный
синтаксис языка, доступный даже для неспециалистов, а также поддержка
самых разных стилей программирования.

Стек научных пакетов
Существует целый набор специализированных пакетов, условно объединяемых в так называемый научный стек. Рассмотрим основные компоненты
данного стека.
NumPy
Библиотека, обеспечивающая поддержку многомерных массивов,
предназначенных для хранения однородных и неоднородных данных.
Включает оптимизированные функции/методы для работы с такими
массивами.

Python как инструмент финансовых расчетов

33

SciPy
Коллекция пакетов, реализующих функции для научных и финансовых
расчетов. В частности, включает функции интерполяции кубических
сплайнов и численного интегрирования.

matplotlib
Один из самых популярных пакетов визуализации данных и построения
как двухмерных, так и трехмерных диаграмм на Python.
pandas
Основанная на NumPy библиотека, предназначенная для обработки и
анализа временных рядов и табличных данных. Тесно интегрирована с
пакетами matplotlib (визуализация данных) и PyTables (хранение и загрузка данных ).

Scikit-learn
Популярный пакет машинного обучения, содержащий унифицирован ные реализации множества алгоритмов, таких как классификация и
кластеризация.

PyTables
Популярная надстройка для работы с файлами формата HDF5. Реализует
оптимизированные операции дискового ввода - вывода.
В зависимости от конкретной задачи могут потребоваться и другие пакеты,
которые зачастую реализуются поверх одного из вышеперечисленных базовых пакетов. Но в целом ключевыми структурными элементами выступают
класс ndarray из пакета NumPy ( глава 4) и класс DataFrame из пакета pandas
( глава 5).
Если рассматривать Python только как язык программирования, то необ ходимо сказать, что есть и другие языки, которые могут составить ему конкуренцию в плане синтаксиса и эффективности, например Ruby. На официальном сайте ( www . ruby - lang . org ) он характеризуется следующим образом.
Динамический язык программирования с открытым исходным кодом , для которого характерны простота и высокая производительность. Обладает элегант ным синтаксисом, благодаря которому код легко читать и так же легко писать.

Те, кто программировали на Python, наверняка согласятся с тем, что ана логичным образом можно охарактеризовать и этот язык. Но что отличает
Python от конкурентов наподобие Ruby, так это наличие научного стека, благодаря которому можно отказаться от специализированных языков и пакетов

34

Глава 1

типа Matlab и R. Кроме того, в языке по умолчанию имеется все необходимое,
к примеру, для опытного веб- разработчика или системного администратора.
К тому же Python отлично взаимодействует со специализированными языка ми типа R, поэтому вопрос обычно не в том,выбирать Python или нет, а в том,
какой из языков разработки будет основным в конкретном проекте.

Технологии в финансовой отрасли
Получив общее представление о возможностях Python, поговорим о роли
современных технологий в финансовом анализе. Нас в первую очередь будут
интересовать то, какое влияние Python оказывает и, что самое важное, будет
оказывать на развитие финансовой индустрии.
По сути, в применении передовых технологий в финансовой отрасли нет
ничего необычного. Однако недавние инновации, а также законодательные
изменения привели к тому, что банки и различные финансовые учреждения,
например хедж-фонды, все больше стали превращаться из финансовых посредников в технологические компании. Технологическая база начала рассма триваться как один из ключевых активов для большинства финансовых ком паний по всему миру, что дает как преимущества , так и недостатки. Следует
разобраться, почему так происходит.

Инвестиции в технологии
Банки и другие финансовые учреждения образуют целую индустрию, которая ежегодно тратит огромные средства на передовые технологии. Следующая
цитата показывает не только важность технологий для финансовой отрасли,
но и важность самой этой отрасли для технологического сектора.
Согласно аналитическим данным , собранным компанией IDC ( International
Data Corporation ), к 2021 году общемировые затраты финансового сектора
на информационные технологии вырастут до 500 млрд долл, по сравнению с
440 млрд долл , в 2018 году.
IDC ( июнь 2018 г.)

Банки и финансовые учреждения уже давно втянуты в гонку, направлен ную на переход к цифровой модели управления.
Согласно прогнозам в 2017 году банковские инвестиции в передовые технологии в Северной Америке должны составить около 19,9 млрд долл.

Python как инструмент финансовых расчетов

35

Банки разрабатывают современные информационные системы и новые техно логические решения с целью получения конкурентных преимуществ на гло бальном рынке и привлечения клиентов, ориентированных на использование
онлайн -служб и мобильных приложений. Для глобальных финансово- технологических компаний это прекрасная возможность предложить новые идеи и
программные решения банковской индустрии.
Statista

Транснациональные банки задействуют тысячи разработчиков для обслуживания существующих систем и создания новых. Крупные инвестиционные
компании, сталкиваясь с ростом технологических требований, зачастую имеют бюджет IT- направления в несколько миллиардов долларов.

Технологии как движущая сила
Развитие технологий способствует внедрению инноваций и повышению
эффективности финансового сектора. Как правило, проекты в данной сфере
следуют концепции полного перехода на цифровые модели.
За последние несколько лет финансовая индустрия претерпела радикальные
технологические изменения. Многие руководители требуют от своих IT-отделов повышать эффективность и внедрять качественно новые, инновационные
услуги , при этом поддерживая существующие системы, но снижая затраты на
их эксплуатацию. А тем временем на рынок все активнее выходят финтех- стартапы, предлагая пользовательские системы, разработанные с нуля, а не основанные на унаследованных решениях.
PwC 19th Annual Global CEO Survey, 20161

Побочным эффектом повышения эффективности становится то, что кон курентные преимущества приходится искать во все более сложных решениях.
Это неизбежно влечет за собой рост рисков и затрат, связанных с соблюдени ем регуляторных требований. Финансовый кризис 2007-2008 годов показал,
какую опасность могут нести новые технологии. С аналогичными технологическими рисками сталкивается и финансовый сектор, что нагляднее всего
проявилось в обвале фондового рынка, который имел место в мае 2010 года,
когда вследствие применения автоматизированных биржевых систем обрушение индексов достигло триллионного масштаба. ( Вопросы, связанные с алгоритмической торговлей финансовыми инструментами, будут рассматриваться в части IV.)
https : / / pwc . to / 10YT02d .

36

Глава 1

Технологии и кадры решают все
Как видим, с одной стороны, развитие технологий ведет к снижению за трат, а с другой только постоянные инвестиции в разработку и внедрение
новых решений позволяют финансовым компаниям добиваться конкурентных преимуществ и укреплять свои позиции на рынке. Во многих областях
крупномасштабные инвестиции в технологии и квалифицированный персо нал служат ключом к выживанию на рынке. В качестве примера рассмотрим
рынок анализа деривативов.



В среднем , если рассматривать весь цикл существования программного обеспе чения, фирмы , внедряющие собственные стратегии внебиржевого управления
деривативами, должны вложить от 25 до 36 млн долларов только на создание,

поддержку и улучшение соответствующей программной библиотеки.
Дин [ I ] 2



Построение полнофункциональной библиотеки анализа деривативов не
только трудоемкий и затратный процесс, для него еще и требуется наличие
достаточного числа экспертов . К тому же сами эксперты должны распола гать всеми необходимыми технологиями и программными средствами. В этом
отношении современная экосистема Python выглядит очень привлекательно,
снабжая разработчиков намного более эффективными и дешевыми инструментами, чем, к примеру, 10 лет назад. (Тема анализа деривативов рассматри вается в части V, где мы напишем небольшую, но достаточно эффективную и
гибкую библиотеку, задействуя только стандартные пакеты Python.)
Следующая цитата возвращает нас в эпоху LTCM одного их крупнейших
хедж -фондов, история которого завершилась крахом в конце 1990-х.



Мериуэзер вложил 20 млн долл , в разработку передовой компьютерной систе мы и нанял команду первоклассных финансовых инженеров для управления
компанией LTCM, офис которой находился в Гринуиче , штат Коннектикут. Это
была система управления рисками промышленного масштаба.
Паттерсон [ 3]

Те вычислительные мощности, на которые Мериуэзер потратил миллионы
долларов, сегодня можно купить всего за несколько тысяч долларов или поступить еще проще, арендовав их в одной из облачных служб в рамках гибкого
плана. ( О развертывании облачной инфраструктуры для интерактивного фи нансового анализа и разработки приложений на Python будет рассказываться в
главе 2.) Бюджеты подобной профессиональной инфраструктуры составляют
2

См. раздел “ Дополнительные ресурсы ” в конце главы.

Python как инструмент финансовых расчетов

37

от нескольких долларов в месяц. В то же время сами системы биржевой торговли, финансового анализа и управления рисками стали настолько сложны ми, что современным компаниям приходится внедрять - инфраструктуры,
насчитывающие десятки тысяч вычислительных ядер.

^

В погоне за скоростью, производительностью и объемами данных
Если говорить о тех аспектах финансовых вычислений, на которые технологические изменения оказывают наибольшее влияние, то это скорость
и интенсивность финансовых транзакций. Льюис [ 2] описывает понятие
флеш- трейдинга
процесса высокочастотной биржевой торговли, когда
сделки совершаются с максимально возможной скоростью.
С одной стороны, повышение доступности биржевых данных требует от
финансовых компаний принятия решений в реальном времени. А с другой
внедрение высокочастотной торговли приводит к еще большему росту объемов данных. Оба процесса идут рука об руку, непрерывно снижая временной
масштаб финансовых транзакций. Впервые эта тенденция была выявлена еще
десять лет назад.





В 2008 году фонд Medallion, основанный компанией Renaissance Technologies,
получил небывалую маржу в размере 80%, сумев воспользоваться крайней волатильностью рынка благодаря своим сверхбыстрым компьютерам. Крупней шим бенефициарием в тот год стал Джим Симонс, который положил в карман
солидные 2,5 млрд долл.
Паттерсон [ 3]

Данные о биржевых котировках одного индекса на конец торгов, собран ные за 30 лет, содержатся примерно в 7500 записях. Именно на этих данных
базируются все современные финансовые теории, в частности портфельная
теория Марковица ( Mean -variance Portfolio Theory MPT), модель ценообра зования капитальных активов ( Capital Asset Pricing Model САРМ ) и стоимость под риском (value-at - risk VaR ).
Для сравнения: в обычный торговый день курс акций компании Apple
( AAPL) в среднем изменяется около 15 тыс. раз в час, что в два раза больше,
чем количество котировок, собранных за последних 30 лет. Анализ таких объ-







емов данных сопряжен с целым рядом трудностей.

Обработка данных
Сегодня уже недостаточно обрабатывать только сведения о котировках
акций и других финансовых инструментов на момент закрытия торгов.

38

Глава 1

Слишком много событий происходит в течение каждого торгового дня,
а для некоторых инструментов 24 часа в сутки 7 дней в неделю.



Скорость анализа данных
Биржевые решения должны приниматься за миллисекунды или даже
быстрее, а это означает, что нужны соответствующие аналитические системы, способные обрабатывать огромные объемы данных в реальном
времени.

Теоретическая база
Несмотря на то что традиционные финансовые теории далеки от идеала,
они прошли испытание временем. Но когда речь идет о принятии решений в милли - и микросекундных интервалах, надежных теорий пока
еще недостаточно.
Указанные трудности можно преодолеть только с помощью современных
технологий. Самое удивительное то, что даже отсутствие надежных финан совых теорий часто компенсируется технологическими преимуществами,
поскольку алгоритмы высокочастотной торговли задействуют микроструктурные биржевые элементы ( поток заявок, спреды и т.п.), а не опираются на
теоретические суждения.

Анализ в реальном времени
За последние годы в финансовой сфере резко возросла важность финансового анализа. Это прямое следствие повышения скорости и производительности вычислений, а также увеличения объемов данных. По сути, реакцией
рынка стало появление анализа в реальном времени.
Под финансовым анализом мы понимаем применение современных технологий, программных решений и алгоритмов для получения аналитических
данных или принятия решений. В качестве примера можно привести оцен ку того, какое влияние на продажи окажет изменение стоимости банковского
продукта, или крупномасштабную корректировку кредитной оценки ( Credit
Valuation Adjustment CVA ) для сложных портфелей деривативов крупного
инвестиционного банка .
В этом контексте финансовые учреждения сталкиваются с двумя основны ми трудностями.



Большие данные
Банкам и другим финансовым компаниям приходилось иметь дело с
огромными массивами данных еще до начала эпохи “ больших данных”.

Python как инструмент финансовых расчетов

39

Как бы там ни было, сегодня объемы анализируемых данных настолько
велики, что требуют постоянного увеличения как вычислительной мощности компьютерных систем, так и размера хранилищ данных.
Расчеты в реальном времени
В прошлом инвестиционные решения принимались на основе тщательно спланированных стратегий и процессов управления рисками. В ны нешнюю эпоху решения приходится принимать в реальном времени.
Задачи, которые раньше решались в офисе на протяжении рабочего дня,
сегодня решаются прямо на бирже в процессе торгов.

Здесь снова прослеживается связь между развитием технологий и практи кой ведения бизнеса. С одной стороны, существует постоянная потребность
в повышении скорости и точности аналитических решений за счет внедрения
современных технологий. А с другой стороны, появление новых технологий
делает возможными новые аналитические подходы, которые всего несколько
лет назад считались невозможными ( или нереалистичными исходя из бюджетных соображений ).
Одной из ключевых тенденций в сфере финансового анализа стало внедрение параллельных архитектур многоядерных вычислений на стороне центрального процессора и массово-параллельных архитектур на основе графи ческих процессоров. Современные графические процессоры содержат тысячи
вычислительных ядер, что заставляет переосмысливать алгоритмы парал лельных вычислений . Но для того чтобы извлечь преимущества из новых
аппаратных платформ, пользователям приходится осваивать новые языки и

парадигмы программирования.

Python для финансовых расчетов
В предыдущем разделе рассматривались основные аспекты применения
современных технологий в финансовой среде:




стоимость технологий для финансовой индустрии;



технологии и квалифицированные кадры как барьеры для выхода на фи нансовый рынок;

технологии как движущая сила бизнеса и инноваций;

• увеличение скорости

и интенсивности вычислений, а также объемов

данных;

• необходимость анализа данных в реальном времени.
40

Глава 1

В этом разделе вы узнаете о том, как Python помогает преодолевать возни кающие проблемы . Но для начала следует познакомиться с общеязыковыми
инструментами и синтаксисом Python.

Синтаксис Python, применяемый в финансовых вычислениях
Большинство разработчиков, которые только начинают знакомиться с ин струментами Python, применяемыми в финансовых расчетах, неизбежно стал киваются с алгоритмическими проблемами. То же самое будет испытывать
ученый, которому нужно решить дифференциальное уравнение, вычислить
интеграл или просто визуализировать данные. На этом этапе еще никто не
думает о формальном процессе разработки, тестировании, документации или
развертывании готового приложения. И именно здесь люди начинают ценить
Python как язык программирования. Причина заключается в том, что синтак сис Python в достаточной степени приближен к математическому синтаксису,
применяемому для решения научных и финансовых задач.
В качестве иллюстрации можно взять любой финансовый алгоритм, напри мер оценку европейского колл -опциона методом Монте- Карло. Предположим,
применяется модель Блэка
Шоулза
Мертона ( Black-Scholes-Merton
)
,
BSM в которой рисковость опциона описывается через геометрическое броуновское движение.
Примем следующие значения параметров:









начальный уровень индекса, SQ = 100;

• страйк- цена опциона, К = 105;
• срок исполнения опциона, Т = 1 год;

• безрисковая краткосрочная ставка, г = 0,05;
• волатильность доходности актива, о = 0,2.





В модели Блэка Шоулза Мертона уровень индекса при экспирации оп циона представляется случайной величиной, рассчитываемой по следующей
формуле, в которой случайная переменная z имеет нормальное распределение
вероятностей ( уравнение 1.1).

.

Уравнение 1.1 Уровень индекса при экспирации опциона в модели Блэка
Шоулза Мертона



/V

ST = S0 exp

w

\

г — — сг2 T +

2

/

CRVRZ



Л
/

Python как инструмент финансовых расчетов

41

Ниже приведено алгоритмическое описание процедуры оценки опциона
по методу Монте-Карло.

.

1 Образуйте множество I псевдослучайных чисел z( i )9 / е {1, 2,
стандартного нормального распределения.

/ } из

2. Вычислите уровень индекса при экспирации опциона ST( i ) для всех заданных z( i ) y воспользовавшись формулой из уравнения 1.1.

.

3 Вычислите внутренние стоимости по следующему уравнению:

hT ( i ) = таx( ST ( i ) - К , 0) .

.

4 Определите текущую стоимость опциона по методу Монте-Карло (урав нение 1.2).

.

Уравнение 1.2 Расчет стоимости европейского опциона по методу
Монте-Карло

с0 * erT

\Y.hT
1

I

( i )-

Следующим этапом будет перевод алгоритма на язык Python. Решение выглядит следующим образом.
In [ 6 ] : import math
import numpy as np

О

In [ 7 ] : S0 = 100 , ©
К = 105 . ©
T = 1, 0 ©
г = 0.05 ©
sigma = 0 . 2 ©
In [ 8 ] : I = 1.00000

©

In [ 9 ] : np . random . seed( 1000 )

©

In [ 10 ] : z = np . random . standard_normal (I)

In [ 11] : ST = SO

*

np . exp ( ( r - sigma ** 2 / 2 ) * T + sigma *
math . sqrt ( T ) * z ) ©

In [ 12 ] : hT = np . maximum ( ST - K , 0 )

42

Глава 1

0

©

In [ 13 ] : СО

= math . exp ( - r *

T ) * np . mean ( hT )

©

In [14 ] : print ( ' Стоимость европейского колл - опциона :
{ : 5.3 f } . ' . format ( CO ) ) ©
Стоимость европейского колл - опциона : 8.019 .

О Финансовые расчеты в данном случае выполняются средствами пакета
NumPy.

© Задаем параметры модели.
© Фиксируем затравочное значение для генератора случайных чисел.

© Формируем множество случайных чисел со стандартным распределением вероятности.

© Вычисляем цену актива при экспирации опциона.

© Вычисляем премию по опциону.
© Оценка опциона по методу Монте- Карло.
© Выводим результат оценки на экран.
Рассмотрим ключевые моменты.
Понятный синтаксис
Синтаксис Python действительно схож с математическим языком, что
особенно заметно при задании параметров модели.

Трансляция задачи
Каждое математическое уравнение и каждый шаг алгоритма представляется в коде Python одной инструкцией.
Векторизация
Одно из достоинств пакета NumPy



компактный векторизованный
синтаксис, что позволяет выполнить, к примеру, 100 000 вычислений с
помощью одной команды.

Приведенный выше код можно легко выполнить в интерактивной среде,
такой как IPython или Jupyter Notebook. Но если код планируется выполнять
на регулярной основе, его следует сохранить в виде модуля ( сценария ), представляющего собой отдельный файл Python (с технической точки зрения простой текстовый документ ) с расширением .ру. В данном случае модуль можно
сохранить в файле bsm mcs euro.py ( листинг 1.1).

_ _

Python как инструмент финансовых расчетов

43

.

Листинг 1 L Оценка европейского колл-опциона по методу Монте-Карло
#
# Оценка европейского колл - опциона
# ( модель Блэка - Ыоулза - Мертона )
# bsm_ mcs euro py

_

# Python
# ( c ) Dr
#

.

for Finance , 2nd ed .

.

Yves J

.

Hilpisch

import math
import numpy as np
# Значения параметров
# начальный уровень индекса
SO = 100.
# страйк - цена опциона
К = 105
# срок исполнения опциона
T = 1.0
# безрисковая краткосрочная ставка
г = 0.05
sigma = 0 . 2 # волатильность

.

I = 100000

# количество этапов моделирования

# Алгоритм оценки
г = np . random . standard_normal (I)

# генерация псевдослучайных чисел

# Цена актива при экспирации опциона
ST = S 0 * пр . ехр ( ( г - 0.5 * sigma ** 2 ) * Т + sigma *
math . sqrt ( T ) * z )
# премия no опциону
hT = np . maximum( ST
К , 0)
СО = math . exp ( - r * Т ) * np . mean ( hT ) # оценка по методу Монте - Карло

-

# Вывод результата

print ( Стоимость европейского колл - опциона : %5 . 3 f . * % СО )
1

Данный алгоритмический пример наглядно демонстрирует, насколько удобен синтаксис Python для выполнения научных расчетов. Он позволяет легко
перейти от постановки задачи и ее математического описания к программной
реализации. Порядок действий выглядит так.



Обычный язык. Позволяет в устном и письменном виде выразить суть



Математический язык. Позволяет максимально четко и лаконично
описать и смоделировать абстрактные аспекты задачи, алгоритм ее ре-

научной или финансовой задачи.

шения, числовые параметры и т.п.

44

Глава 1

• Python. Позволяет технически смоделировать и реализовать абстрактные аспекты задачи, алгоритм ее решения, числовые параметры и т. п.

(

к

Математические аспекты синтаксиса Python
Вряд ли вам удастся найти другой язык программирования, на столько близкий синтаксически к языку математики, как Python.
В большинстве случаев математические выражения и формулы
транслируются в Python почти в неизменном виде, что делает его
невероятно эффективным языком разработки и написания приложений для финансовых расчетов.

Нередко в применяемый стек языков добавляется четвертый компонент:
псевдокод. Его роль заключается в представлении математических формул, лежащих в основе финансовых расчетов, в наиболее понятном для программной
реализации виде. Помимо самого алгоритма, псевдокод должен учитывать
особенности обработки данных компьютером.
Практика применения псевдокода возникла вследствие огромного разли чия между формальным математическим представлением задачи и его описанием в большинстве компилируемых языков программирования. Очень
часто написанный на таких языках код настолько усложняется добавлением
обязательных технических элементов, что его затруднительно сопоставить с
исходными математическими выкладками.
К счастью, при работе с Python отдельный этап составления псевдокода
не нужен, поскольку синтаксис языка почти аналогичен языку математики, а
чисто технические дополнения сведены к минимуму. Для этого в Python предусмотрено множество высокоуровневых конструкций. Конечно, у подобного
подхода есть и недостатки, но в целом можно сказать, что всякий раз, когда
возникает такая необходимость, в Python можно следовать строгим процедурам кодирования, как и в любых других языках программирования . В этом
смысле Python берет все лучшее из двух миров: высокоуровневую абстракцию
и строгую реализацию.

Эффективность и производительность кода Python
Если рассматривать ситуацию на высоком уровне, то преимущества Python
можно оценить по следующим аспектам.
Эффективность
Поможет ли Python получить результаты быстрее и дешевле ?

Python как инструмент финансовых расчетов

45

Производительность
Позволит ли Python достичь большего при тех же самых ресурсах ( люди,
средства и т.п.) ?
Качество
Что такого позволяет делать Python, чего не могут дать другие технологии ?

Навряд ли на такие вопросы можно дать исчерпывающие ответы, но все же
приведем ряд аргументов.

Высокая скорость вычислений



Область, в которой эффективность Python становится очевидной,
это
интерактивный анализ данных. Именно здесь проявляются преимущества
таких мощных инструментов, как IPython, Jupyter Notebook и программных
пакетов наподобие pandas.
Рассмотрим студента экономического факультета, который пишет диплом,
исследуя индекс S&P 500. Ему необходимо проанализировать значения ин декса за несколько лет и попытаться найти доказательства того, что его волатильность, в противоположность принятым во многих финансовых моделях
предположениям, изменяется со временем, а потому ее нельзя выражать константой . Результаты следует представить в графическом виде. Для решения
такой задачи нужно выполнить следующие действия:

• загрузить данные о значениях индекса S&P 500 из Интернета;
• вычислить среднеквадратическое отклонение логарифмической доходности (волатильности ) в годовом разрезе;

• построить графики изменения индекса S&P 500 и волатильности.
Еще совсем недавно такого рода задачи считались настолько сложными,
что их решение было под силу только профессиональным финансовым ана литикам. Сегодня же с ней легко справится даже студент экономического фа культета. Ниже приведен соответствующий код (на синтаксис можете пока
не обращать внимания мы все детально рассмотрим в последующих главах).



In [16 ] : import пигпру as пр О
import pandas as pd О
from pylab import pit, mpl

©

In [ 17 ] : pit.style.use('seaborn ' ) ©
mpl.rcParamsf 'font.family ' ] = ' serif'
%matplotlib inline

46

Глава 1

©

.

_

_

.. ..

.

/ / source / tr eikon_eod_data csv ' ,
index _col=0, parse_dates = True ) ©
data = pd DataFrame ( data [ ' SPX ' ] ) 0
data dropna ( inplace=True ) О
data info() ©

Datetimelndex : 2138 entries, 2010 - 01- 04 to 2018 - 06 - 29
Data columns ( total 1 columns ) :
2138 non - null float 64
SPX
dtypes : float 64(l)
memory usage: 33.4 KB

In [ 18 ] : data = pd read csv ( '

.

.

.

.

.

.

.

.

.

.

In [ 19 ] : data [ ' Доходность ' ] = np log ( data / data shift (l)) ©
data [ ' Волатильность ' ] =
data [ ' Доходность ' ] rolling( 252 ) std ( ) * np sqrt ( 2.52 )

.

.

.

.

.

In [ 20 ] : data [ [ ' SPX ' , ' Волатильность ' ] ] plot ( subplots= True,
figsize= ( 10, 6 ) );

©
©

О Импорт пакетов NumPy и pandas.

0 Импорт библиотеки matplotlib и настройка стиля диаграмм, отображаемых в среде Jupyter.

0 Метод pd . read__ csv ( ) позволяет загрузить данные из CSV- файла, хранящегося локально или на сервере.
О Формирование подмножества данных и исключение нечисловых значений (NaN).

0 Вывод метаинформации о выбранном наборе данных.

© Вычисление логарифмической доходности в векторизованной форме (без
применения циклов).

© Определение среднегодовой волатильности.

© Визуализация двух временных рядов.
Результат данного интерактивного сеанса представлен на рис. 1.1. Просто
поразительно, как с помощью всего нескольких строк кода нам удалось ре шить три сложные задачи финансового анализа: сбор данных, выполнение
повторяющихся математических вычислений и визуализация результатов.
Обратите внимание на то, что благодаря использованию пакета pandas с
Python как инструмент финансовых расчетов

47

временными рядами можно работать почти так же легко, как и с веществен ными числами.
. SPX
2500
2000
1500

1000

Волатильность
0.20
0.15
0.10

п>

.

^

орО

^

Ъ

\
ф

Date

Рис. 1.1 Уровни индекса S & P 500 и среднегодовая волатильность

Как показывает данный пример, при правильном выборе инструментов и
пакетов Python , предлагающих соответствующие высокоуровневые абстрак ции, финансовый аналитик получает возможность сконцентрироваться на
решении конкретной профессиональной задачи, а не на технических аспектах
программной реализации. Это позволяет аналитику быстрее реагировать на
изменения рыночных условий, делая оценки практически в режиме реального
времени и тем самым опережая своих конкурентов. Таким образом, увеличе ние эффективности может легко давать коммерческий эффект.

Повышение производительности
Как правило, критики признают, что Python обладает понятным синтак сисом, позволяющим писать эффективный код. Однако по своей природе
это интерпретируемый язык , а потому существует предубеждение, будто бы
код Python слишком медленный для выполнения трудоемких финансовых
расчетов. В определенных ситуациях это действительно так, но в реальности
все зависит от выбранного подхода. Никто не мешает вам писать эффективные приложения! Можно выделить три стратегии повышения производи тельности.

48

Глава 1

Идиомы и парадигмы программирования
Python позволяет решать одни и те же задачи разными способами, которые характеризуются разной производительностью. Выбрав правильный способ реализации ( т.е. разумно используя структуры данных,
избегая циклов в рамках векторизации и применяя пакеты наподобие
pandas ), можно существенно повысить скорость любых финансовых
вычислений.
Компиляция кода

Существуют специальные пакеты повышения производительности, содержащие скомпилированные версии важных функций или выполня ющие статическую либо динамическую компиляцию кода Python, что
на порядок ускоряет выполнение важных функций в сравнении с чи стым кодом Python. Самые популярные пакеты такого рода Cython и
Numba.



Параллельные вычисления
Многие вычислительные процессы, особенно в финансовой области,
можно существенно ускорить за счет параллельного выполнения.
В Python это можно сделать очень легко.

а

Высокопроизводительные вычисления на Python
Сам по себе Python нельзя назвать высокопроизводительным язы ком. Но он превратился в идеальную платформу для доступа к
производительным технологиям , что делает его своего рода связующим языком.

Рассмотрим простой, но реалистичный пример, в котором задействуются
все три вышеуказанные стратегии ( подробнее они рассматриваются в последующих главах). В финансовом анализе часто возникает задача оценки сложных математических выражений применительно к большим числовым масси вам. В Python для этого есть собственные инструменты.
In [21]: import math
loops = 2500800
a = range ( l , loops )
def f ( x ) :
return 3 * math . log ( x ) + math . cos ( x ) ** 2
%timeit г = [ f ( x ) for x in a ]
1.59 s ± 41.2 ms per loop ( mean ± std . dev . of 7 runs ,
1 loop each )

Python как инструмент финансовых расчетов

49

Как видите, на выполнение функции f ( ) 2,5 млн раз интерпретатору Python
потребовалось примерно 1,6 с. Ту же самую задачу можно решить с помощью
оптимизированных ( т.е. предварительно откомпилированных) функций пакета NumPy.
In [ 2 2 ] : import numpy as np
a = np . arange ( l , loops )
%timeit г = 3 * np . log ( a ) + np . cos ( a ) ** 2
87.9 ms ± 1.73 ms per loop ( mean ± std . dev . of 7 runs ,
10 loops each )

В результате время выполнения кода сократилось до 88 мс. Впрочем, в
Python есть специальный программный пакет для решения таких задач:
питехрг. Он компилирует все выражение, чтобы добиться еще большей, чем
позволяет NumPy, производительности. Это, в частности, достигается за счет
предотвращения хранения в памяти множественных копий объектов ndarray.
In [ 2 3 ] : import numexpr as ne
ne . set _ num threads ( l )
f = ' 3 * log ( a ) + cos ( a ) ** 2 '
%timeit г = ne . evaluate ( f )
50.6 ms ± 4.2 ms per loop ( mean ± std . dev . of 7 runs ,
10 loops each )

_

Данный подход позволяет сократить время обработки данных еще сильнее
до 50 мс. Наряду с этим пакет numexpr располагает средствами распа раллеливания вычислений, что позволяет задействовать множественные потоки центрального процессора.



In [ 24 ] : ne . set _ num_ threads ( 4 )
%timeit г = ne . evaluate ( f )
22.8 ms ± 1.76 ms per loop ( mean ± std . dev . of 7 runs ,
10 loops each )

Параллелизация дополнительно сокращает время выполнения кода до
23 мс за счет применения четырех потоков. Таким образом, проведенная
нами оптимизация позволила повысить производительность более чем в 90

раз. Причем это стало возможным без изменения базового алгоритма и без
знания алгоритмов компиляции или параллельных вычислений. Такого рода
программные средства доступны на высоком уровне даже для пользователей,
не являющихся экспертами в Python . Но, разумеется, необходимо знать, какие
средства есть в вашем распоряжении.
Рассмотренный пример показывает, что Python располагает целым рядом
средств, позволяющих извлечь максимум из имеющихся ресурсов. Тем самым
50

Глава 1

мы получаем повышение производительности. При параллельном подходе, в
отличие от последовательного, за то же самое время можно выполнить в 6 раз
больше вычислений. Для этого достаточно указать интерпретатору на необходимость использования всех потоков центрального процессора, а не только
одного.

От прототипа к готовому приложению
Эффективность интерактивного анализа и высокая скорость вычислений два ключевых преимущества Python. Но есть и еще одно, не очевидное
на первый взгляд преимущество, которое может иметь стратегическое значение с точки зрения финансовых вычислений. Речь идет о возможности использовать Python на протяжении всего производственного цикла, от создания
прототипа до выпуска готового приложения.
Практика современных финансовых компаний по всему миру, когда речь
идет о разработке финансовых приложений, зачастую подразумевает двухэтапный процесс. На первом этапе к работе подключаются специалисты по
финансовой математике (“ кванты ” ), отвечающие за разработку модели и технического прототипа. Они предпочитают инструменты типа Matlab и R, удоб ные для быстрой интерактивной разработки приложений. На данной стадии
вопросы производительности, стабильности, развертывания инфраструктуры, контроля доступа и управления версиями не имеют принципиального
значения. Речь идет лишь о получении принципиально работоспособной модели и прототипа, соответствующего ключевым требованиям алгоритма или
итогового приложения.
Как только прототип готов, в игру вступают разработчики из 1Т-отдела,
которые отвечают за перевод кода прототипа в надежный, управляемый и
производительный производственный код. Как правило, на этом этапе происходит сдвиг парадигмы, поскольку требованиям развертывания программной
инфраструктуры соответствуют компилируемые языки, такие как C++ или
Java. Кроме того, применяется формальный процесс разработки с задействованием профессиональных инструментов, систем управления версиями и пр.
Описанный двухэтапный процесс имеет ряд принципиальных недостатков.



Низкая эффективность
Код прототипа не подлежит повторному использованию, и алгоритмы
приходится реализовывать дважды, что приводит к дополнительным
тратам времени и ресурсов. Кроме того, при переписывании кода прототипа могут возникать ошибки .

Python как инструмент финансовых расчетов

51

Квалификация исполнителей
Специалисты из разных отделов обладают разными навыками и применяют разные языки для разработки одного и того же. Более того, они
даже выражают свои мысли по- разному.
Унаследованный код
Полученный код приходится обслуживать на разных языках и платформах.

Python позволяет упростить весь цикл разработки, поскольку один набор
инструментов применяется от первых интерактивных попыток получить рабочий прототип и до финального выпуска надежного и эффективного производственного кода. Упрощается не только взаимодействие специалистов
из разных департаментов, но и обучение персонала, поскольку на всех этапах
разработки финансового приложения задействуется один язык программи рования. Устраняется также неизбежная избыточность и неэффективность,
связанная с применением разных технологий на разных этапах процесса разработки. В целом Python предлагает согласованную технологическую среду,
пригодную для решения любых задач финансового анализа, разработки фи нансовых приложений и реализации алгоритмов.

Финансовые расчеты на основе данных
и искусственного интеллекта
Все те соображения касательно применения технологий в финансовой от расли, которые были сформулированы в первом издании книги, вышедшем
в 2014 году, справедливы и сейчас. Но в последние годы в финансовой сфере
проявились две важнейшие тенденции, которые несут фундаментальные изменения.

Финансовые системы, управляемые данными
Некоторые фундаментальные финансовые теории, например портфельная
MPT) и модель ценообразова ния капитальных активов ( Capital Asset Pricing Model САРМ ) , разрабаты вались еще в середине XX века. Они по- прежнему остаются краеугольным
камнем образовательных программ, по которым студенты изучают экономи ку, финансы, финансовую инженерию и деловое администрирование. И это в
определенной степени удивительно, так как их доказательная база достаточно

теория Марковица (Modern Portfolio Theory

52

Глава 1





скудная, если не сказать больше, а имеющиеся наблюдения часто вступают в
противоречие с тем, что утверждает та или иная теория. С другой стороны,
они обладают очевидной популярностью, поскольку близки к нашему пони манию того, как должны работать финансовые рынки. Плюс это все- таки кра сивые математические теории, построенные на достаточно логичных, хоть и
слишком упрощенных, рассуждениях.
Научный методу применяемый, к примеру, в физике, базируется на данных ^
собираемых в ходе наблюдений и экспериментов. Только после этого строятся
гипотезы и теории, которые затем снова проверяются на данных. Если науч ный тест подтверждает предположения ученых, то гипотеза или теория долж ным образом формализуется в виде диссертации либо академической публи кации. Если же тест не дал результатов, то гипотеза или теория отвергается,
и ученые начинают искать новые объяснения, которые не противоречили бы
имеющимся данным. Поскольку законы физики фундаментальны и не меня ются, открытие того или иного закона приводит к тому, что он становится
частью наших знаний об окружающем мире на столетия вперед.
Финансовые теории исторически шли вразрез с научным методом. Зача стую их разрабатывали “ от печки” на основе упрощенных математических моделей с целью поиска элегантных ответов на ключевые вопросы финансового
анализа . Одно из популярных допущений
предположение о нормальном
распределении доходности финансовых инструментов и линейной зависи мости между процентными ставками. Но поскольку такого рода вещи редко
наблюдаются на финансовых рынках, не удивительно, что эмпирических до казательств красивых теорий зачастую нет. Многие финансовые теории и модели сначала были сформулированы и описаны в научных изданиях и только
впоследствии были проверены на реальных данных. В определенной степени
это связано с тем, что в 1950-1970-е годы ( и даже в следующие десятилетия )
исследователи не располагали тем объемом финансовых данных, который сегодня есть даже у студентов-бакалавров.
Ситуация с доступностью финансовых данных радикально изменилась в
середине 1990-х годов, и сегодня любой исследователь может получить доступ
к огромным объемам исторических данных, а также к биржевым данным в
реальном времени через потоковые службы. Это дает нам возможность вернуться к научному методу и начать с исследования данных, что всегда должно
предшествовать появлению идей, гипотез, моделей и стратегий.
Следующий простой пример показывает, насколько легко в наши дни получить любые финансовые данные за интересующий период времени, используя только Python и подписку на программный интерфейс Eikon (https : / /



Python как инструмент финансовых расчетов

53

developers . refinitiv . com / eikon - apis / eikon - data - apis[ ] ) . Приведенный
ниже код получает сведения о колебаниях курса акций компании Apple в течение одного часа дневных торгов. Всего загружается 15 000 записей, включая
данные об объемах сделок. Стандартный биржевой тикер Apple AAPL, но в
классификации RIC ( Reuters Instrument Code) он обозначается как AAPL .0.



In [26]: import eikon as ek

О

_

In [27]: data = ek . get timeseries ( ' AAPL . 0 ' , fields = ' * ' ,
start _date = ' 2018 - 10 - 18 16 : 00 : 00 ' ,
end _date = ' 2018 - 10 - 18 17 : 00 : 00 ' ,
intervals ' tick ' ) ©
In [28]: data . infoQ ©
< class ' pandas . core . frame . DataFrame ' >
Datetimelndex : 35350 entries , 2018 - 10 - 18 16 : 00 : 00.002000 to
2018 - 10 - 18 16 : 59 : 59.888000
Data columns ( total 2 columns ) :
VALUE
35285 non - null float 64
VOLUME
35350 non - null float 64
dtypes : float 64 ( 2 )
memory usage : 828.5 KB
In [29]: data . tailO
Out[29]:

-

2018 10-18
2018 10-18
2018-10 18
2018-10-18
2018-10 18

-

©

AAPL.0

VALUE

VOLUME

Date
16:59:59.433
16:59:59.433
16:59:59.439
16:59:59.754
16:59:59.888

217.13
217.13
217.13
217.14
217.13

10.0
12.0
231.0
100.0
100.0

О Для работы с программным интерфейсом Eikon требуется подписка на
службу и наличие подключения к серверу.

© Получение котировок акций Apple ( AAPL . 0).
© Вывод последних пяти записей из таблицы котировок.
Программный интерфейс Eikon позволяет получить доступ не только к
структурированным финансовым данным, например к исторической ин формации о стоимости акций, но и к неструктурированным данным, таким
как новости. В следующем примере показано, как получить метаданные

54

Глава 1

небольшой подборки новостей и вывести на экран начальный фрагмент одной из статей.
In [30]: news = ek.get news headlines(' R:AAPL.O Language:LEN ' ,

_

_

_
_

date from= ' 2018 -05 -01',
date to= ' 2018 -06 - 29',
count=7) О

In [31]: news
0ut[31]:

--

2018
2018
2018
2018
2018
2018
2018

-

06
06
06
06
06
06
06

---

28
28
28
28
28
28
28

-- - - -

2018
2018
2018
2018
2018
2018
2018

06
06
06
06
06
06
06

28
28
28
28
28
28
28

О
23:00:00.000
21:23:26.526
19:48:32.627
17:33:10.306
17:33:07.033
17:31:44.960
17:00:00.000

versionCreated \
2018 06 28 23:00:00.000
2018 06 28 21:23:26.526
2018 06 28 19:48:32.627
2018 06 28 17:33:10.306
2018 06 - 28 17:33:07.033
2018 06 28 17:31:44.960
2018 - 06 - 28 17:00:00.000

--

--

text

23:00:00.000
21:23:26.526
19:48:32.627
17:33:10.306
17:33:07.033
17:31:44.960
17:00:00.000

RPT - F0CUS - AI ambulances and robot doc...
Why Investors Should Love Apple's (AA...
Reuters Insider - Trump: We ' re reclai...
Apple v. Samsung ends not with a whim...
Apple ' s trade - war discount extended f...
Other Products: Apple's fast - growing ...
Pokemon Go creator plans to sell the ...
storyld

-- --

\

2018 06 28 23:00:00.000 urn:newsml:reuters.com:20180628:nL4NlTU4F8:6
2018 06 28 21:23:26.526 urn:newsml:reuters.com:20180628:nNRA6e2vft:1
2018 06 28 19:48:32.627 urn:newsml:reuters.com:20180628:nRTVlvNwlp:1
2018 06 28 17:33:10.306 urn:newsml:reuters.com:20180628:nNRA6eloza:1
2018 06 28 17:33:07.033 urn:newsml:reuters.com:20180628:nNRA6elpmv:1
2018 06 28 17:31:44.960 urn:newsml:reuters.com:20180628:nNRA6elm3n:1
2018 06 28 17:00:00.000 urn:newsml:reuters.com:20180628:nLlNITUOPC:3

-

-

- -

:00.000
-0606-2828 2123::00
26.526
-06-28 19:4823::32.627
-06-28 17:33:10.306
- -06-28 17:33:07.033

2018
2018
2018
2018
2018

sourceCode

NS:RTRS
NS:ZACKSC
NS:CNBC
NS:WALLST
NS:WALLST

Python как инструмент финансовых расчетов

55

- -

2018 06 28 17:31:44.960
2018-06- 28 17:00:00.000

NS:WALLST
NS:RTRS

In [32]: story _ htnl = ek.get _ news _ story(news.iloc[l, 2])
In [33]: from bs4 import BeautifulSoup

©

©

In [34]: story = BeautifulSoup(story _ html, ' html5lib').get _ text()

0

In [35]: print(story[83:958]) ©
Jun 28, 2018 For years, investors and Apple AAPL have been
beholden to the iPhone, which is hardly a negative since its
flagship product is largely responsible for turning Apple
into one of the world 's biggest companies. But Apple has
slowly pushed into new growth areas, with streaming
television its newest frontier. So let ' s take a look at what
Apple has planned as it readies itself to compete against the
likes of Netflix NFLX and Amazon AMZN in the battle for the
new age of entertainment. Apple's second - quarter revenues
jumped by 16% to reach $61.14 billion, with iPhone revenues
up 14%. However, iPhone unit sales climbed only 3% and iPhone
revenues accounted for over 62% of total Q 2 sales. Apple
knows this is not a sustainable business model, because rare
is the consumer product that can remain in vogue for decades.
This is why Apple has made a big push into news,

О Получение метаданных небольшой подборки новостей.
0 Получение полного текста отдельной статьи в виде HTML-документа.

0 Импорт библиотеки BeautifulSoup, отвечающей за

синтаксический ана-

лиз HTML-файлов.

© Извлечение содержимого статьи в текстовом виде.
@

Вывод начала статьи.

Несмотря на простоту, эти примеры демонстрируют, насколько легко с помощью пакетов Python и служб подписки можно получить доступ к структурированным и неструктурированным историческим финансовым данным.
Иногда такого рода информация бывает доступна даже бесплатно через торговые площадки наподобие FXCM ( будет рассматриваться в главах 14 и 16).
Откуда бы ни поступали наборы данных, после успешного импорта к ним
можно применять весь спектр аналитических инструментов Python.
56

Глава 1

1

1

^

I Финансовые системы, управляемые данными

^

Современные финансовые системы управляются данными. Даже
крупнейшие и самые прибыльные хедж- фонды делают акцент на
m данных, а не собственно на финансах. Финансовые данные становятся доступными во все более крупных масштабах, причем как
для коммерческих клиентов, так и для рядовых пользователей.
В свою очередь, Python является языком выбора для взаимодей ствия с соответствующими программными интерфейсами и ана лиза получаемых данных.

щ\

^

Финансовые системы на основе искусственного интеллекта
Благодаря доступности больших объемов финансовых данных через программные интерфейсы стало возможным применять методы искусственного
интеллекта, а также машинного и глубокого обучения для решения финансовых задач, таких как, например, алгоритмическая торговля.
идеальный язык для задач искусственного интеллекта, к кото Python
рому в первую очередь обращаются исследователи и разработчики. В этом
смысле финансовая отрасль получает дополнительные преимущества, пользуясь имеющимися наработками в самых разных областях, иногда совершен но не связанных с финансами. В качестве примера можно взять открытую
нейросетевую библиотеку TensorFlow ( www . tensorflow . org ) ,разработанную
и сопровождаемую компанией Google. Эта же библиотека применяется ком панией Alphabet ( владельцем Google) для разработки беспилотных автомобилей.
Несмотря на то что цели TensorFlow даже косвенно не связаны с задача ми автоматизированной алгоритмической биржевой торговли, с помощью
TensorFlow можно, например, предсказывать волатильность финансовых
рынков ( соответствующие примеры будут приведены в главе 15).
Scikit Один из самых популярных пакетов для машинного обучения
learn. В следующем примере упрощенно показано, как применять алгоритмы
классификации для предсказания того, в каком направлении будут меняться
рыночные цены, и как на основе этих предсказаний построить стратегию ал горитмической торговли. Все детали будут объясняться в главе 15, поэтому
пример дан в максимально компактном виде. Для начала необходимо импортировать данные и подготовить массив признаков ( накопительные показатели
логарифмической доходности с векторами смещений ).





In [ 36 ] : import numpy as np
import pandas as pd

Python как инструмент финансовых расчетов

57

.
. . / .. / source / t r _eikon_eod _data . csv ' ,
index _c o l=8, parse_dates=True )
data = pd . DataFrane( data [ ' AAPL.O ' ] ) О
data [ ' Доходность ' ] = np .l o g( data / data. shiftQ ) ©

I n [ 37 ] : data = pd read_ csv ( '

.

data dropna ( inplace=T rue )
I n [ 38 ] : lags = 6
I n [ 39 ] : cols = [ ]

for l a g i n range (l, lags + 1) :
c o l = ’ lag {} ' . format ( l a g )
data [ c o l] = np . sign( data [ ' Доходность ' ] . s h i f t ( l a g ) )
cols . append ( c o l )
data dropna ( inplace=T rue )

_

©

.

О Получение исторических котировок акций компании Apple (AAPL. 0).

© Вычисление логарифмической доходности за весь период.
© Генерирование столбцов объекта Data Frame и заполнение их направленными показателями логарифмической доходности (+1или - 1).
Далее создается объект модели для метода опорных векторов (Support
Vector Machine — SVM), после чего выполняется обучение модели и строится
прогноз. На рис. 1.2 можно увидеть, что торговая стратегия, основанная на
полученном прогнозе, применительно к акциям компании Apple дает лучший
результат, чем инвестиция на основе простой доходности.

.

I n [ 40 ] : from sklearn svm import SVC

I n [ 41] : model = SVC ( gamma= ' scale ' )

.

©

.

I n [ 42 ] : model f i t ( data [ cols ] , np sign( data [ ' Доходность ' ] ) ) ©
0u t [ 42 ] : SVC ( C=1 . 0, cache size= 20O, class _ weight=None, coef 0=0 . 0,
decision_ function_ shape= ' ovr ' , degree=3, gamma= ' scale ' ,
kernel= ' r b f ' , max _i t e r = -l, probability=False,
random s t a t e=None, shrinking=True, t o l=0.001,
verbose=False )

_

_

.

I n [ 43 ] : data [ ' Прогноз ' ] = model predict ( data [ cols ] )
I n [ 44 ] : data [ ' Стратегия ' ] = data [ ' Прогноз ' ]

58

Глава 1

©

* data [ ' Доходность ' ] ©

In [ 45 ] : data [ [ ' Доходность ' , ' Стратегия ' ]] . cumsumQ . apply ( np . exp ) . plot (
figsize= ( 10 , 6 ) ) ; ©

О Создание объекта модели.

© Обучение модели на основе имеющихся признаков и размеченных данных ( с учетом векторов направлений ).

© Применение обученной модели для создания прогнозов (в пределах выборки), которые становятся позициями торговой стратегии.

© Вычисление логарифмической доходности по торговой стратегии с учетом прогнозных значений и эталонных значений доходности.

© Построение графика доходности торговой стратегии, основанной на ал -

горитмах машинного обучения, в сравнении с инвестицией на основе
простой доходности.
Доходность

12

Стратегия

10

8

6

4

2

Ъ

^ ^

п

1

п

Date

Рис. 1.2. Доходность торговой стратегии, полученной с помощью
алгоритмов машинного обучения, в сравнении с инвестицией в акции

компании Apple на основе простой доходности

Конечно, в этом упрощенном примере не учитываются трансакционные
издержки, а исходный набор данных не разделяется на обучающий и тестовый
наборы. Тем не менее он наглядно показывает, насколько легко применять ал горитмы машинного обучения к финансовым данным, по крайней мере, с технической точки зрения ( на практике придется решить еще ряд важных задач ) .
Python как инструмент финансовых расчетов

59

Г

~

^

1

Финансовые системы на основе искусственного интеллекта

I
\

Технологии искусственного интеллекта вызывают такие же изменения в финансовой отрасли, как и в других областях. Доступность
огромных объемов финансовых данных через программные интерфейсы служит катализатором изменений. Базовые методы искусственного интеллекта, а также машинного и глубокого обучения
будут рассмотрены в главе 13, а в главах 15 и 16 мы научимся применять их в алгоритмической торговле Но учитывайте, что приме нение искусственного интеллекта в финансовых расчетах — тема
отдельной книги.

|

.

Финансовые системы на основе искусственного интеллекта как естественное продолжение финансовых систем, управляемых данными, — привлекательное направление как для исследователей, так и для разработчиков. В книге описывается применение целого ряда методик искусственного интеллекта и
машинного обучения в различных контекстах, но все же основная тема книги,
как следует из ее названия, — фундаментальные методы работы с финансовыми данными с помощью базовых инструментов Python. Эти методы имеют не
меньшее значение и в системах искусственного интеллекта.

Резюме
Язык программирования Python — в особенности его экосистема — служит идеальной технологической базой для разработки финансовых приложений, причем это касается как индустрии в целом, так и отдельных проектов.
Язык обладает целым рядом преимуществ, среди которых понятный синтак сис, эффективные подходы к разработке приложений, а также пригодность
для разработки как прототипов, так и окончательных версий приложений в
рамках единого производственного цикла. Наличие большого количества
свободно доступных пакетов, библиотек и функциональных средств позволяет решать большинство задач, возникающих в современной финансовой среде. Язык соответствует всем требованиям, выдвигаемым с точки зрения анализа данных, работы с большими данными, производительности вычислений,
соответствия регуляторным ограничениям и технологическим стандартам.
Экосистема Python образует целостную, полнофункциональную среду раз работки полного цикла, соответствующую целям даже крупных финансовых

организаций.
Кроме того, Python стал языком выбора в системах искусственного интеллекта, как в целом, так и в частных проектах машинного и глубокого обучения.
60

Глава 1

Таким образом, это правильный язык для создания финансовых приложений,
управляемых данными, а также финансовых систем на основе искусственного
интеллекта два ключевых направления, которые будут определять перспек тивы финансовой отрасли в обозримом будущем.



Дополнительные ресурсы
В следующих книгах более глубоко освещаются темы, поверхностно затронутые в данной главе ( в частности, инструменты Python , анализ деривативов,
машинное обучение и его применение в финансовом анализе).

• Hilpisch, Yves. Derivatives Analytics with Python ( 2015, Wiley).
de Prado, Marcos. Advances in Financial Machine Learning ( 2018,
)
Wiley .

• Lopez



VanderPlas, Jake. Python Data Science Handbook ( 2016, O’ Reilly).

Что касается алгоритмической торговли, то на сайте автора книги предлагается ряд обучающих курсов, посвященных применению Python и ряда других программных инструментов в этой быстро развивающейся сфере:

• http:// pyalgo.tpq .io
• http://certificate.tpq.io
В главе цитировались следующие источники.

.

1 Ding, Cubillas. Optimizing the OTC Pricing and Valuation Infrastructure
( 2010, Celent ).

.
3. Patterson, Scott. The Quants ( 2010, Crown Business).

2 Lewis, Michael. Flash Boys ( 2014, W. W. Norton & Company).

Python как инструмент финансовых расчетов

61

ГЛАВА 2

Инфраструктура Python
Мастерство плотника проявляется в том , что его работа сделана аккуратно и
детали хорошо подогнаны. При этом все, что он сделал, а не только отдельные
части его работы , должно соответствовать плану. Это очень важно.
Миямото Мусаси “ Книга пяти колец”

Для новичков развертывание среды Python выглядит запутанным процессом. То же самое касается установки многочисленных библиотек и пакетов.
Прежде всего, существует множество реализаций Python, включая CPython,
Jython, IronPython и РуРу. Кроме того, необходимо учитывать водораздел
между платформами 2.7 и 3.x1.
Но даже если вы определились с версией Python, развертывание инфраструктуры усложняется целым рядом дополнительных обстоятельств.

• Интерпретатор языка ( в нашем случае — установочный пакет CPython )

исходно поставляется только с так называемой стандартной библиотекой (она, в частности, включает наиболее распространенные математи ческие функции )



Дополнительные пакеты ( а их огромное множество ) нужно устанавли вать отдельно.



Самостоятельная компиляция / сборка нестандартных пакетов непростая задача, поскольку необходимо учитывать межпакетные зависимости и требования конкретной операционной системы.



межпакетных зависимостей и поддержка согласованности версий ( т.е. обслуживание инфраструктуры ) зачастую утомительный и трудоемкий процесс.

• Последующий учет



• Обновление некоторых пакетов приводит к тому, что приходится перекомпилировать множество других пакетов.
1

В книге рассматривается версия CPython 3.7, которая была текущей на момент подготовки
издания. Это наиболее популярная версия языка.

• Обновление или замена одного из пакетов может нарушить работу других программ.

К счастью, в нашем распоряжении имеются специальные инструменты,
призванные помочь в преодолении указанных трудностей. В данной главе мы
рассмотрим следующие технологии развертывания среды Python.

Менеджеры пакетов
Менеджеры пакетов наподобие pip ( https : / / pypi . org / project / pip / )
и conda ( https : / / conda . io / en / latest / int го . html ) предназначены
для установки, обновления и удаления пакетов Python. Они также помогают контролировать согласованность версий различных пакетов.
Менеджеры виртуального окружения
Менеджеры виртуального окружения, такие как virtualenv ( https : / /
pypi . org / project / virtualenv / ) и conda , позволяют одновременно
управлять несколькими дистрибутивами Python ( например, 2.7 и 3.7,
установленными в одной системе) и безопасно тестировать новые рели зы для разработчиков, не опасаясь нарушить рабочую среду2.
Контейнеры
Контейнеры Docker это автономные файловые системы, содержащие
все элементы, которые требуются для запуска определенной программы,
включая программный код, динамические библиотеки и системные ути литы. Например, можно запустить операционную систему Ubuntu 18.04
с установленным в ней дистрибутивом Python 3.7 и соответствующим
программным кодом Python в контейнере Docker, который запущен на
компьютере с macOS или Windows 10.



Облачные экземпляры
Для развертывания финансовых приложений, написанных на Python,
нужна надежная, защищенная и высокопроизводительная среда. Таким
требованиям зачастую отвечает только профессиональная облачная ин фраструктура, предлагаемая в виде облачных экземпляров различного
масштаба. Одним из преимуществ облачного экземпляра ( виртуального
сервера) по сравнению с выделенным сервером является то, что пользователи обычно платят только за операционное время. Другое преимущество заключается в том, что в случае необходимости облачный экзем пляр можно арендовать чуть ли не на минуту, что позволяет создавать
гибкие масштабируемые решения.
2

Утилита pipenv (https : / / github . com / pypa / pipenv) совмещает возможности менеджера пакетов pip и менеджера виртуального окружения virtualenv.

64

Глава 2



Цель главы
помочь вам установить и настроить рабочую среду Python,
оснастив ее всеми необходимыми профессиональными инструментами ана лиза данных и пакетами визуализации. Эта среда послужит фундаментом для
запуска кода Python в последующих главах.

conda как менеджер пакетов
Несмотря на то что утилиту conda можно установить автономно, лучше
воспользоваться компактным дистрибутивом Miniconda, который включает
conda как менеджер пакетов и виртуального окружения.

Установка Miniconda
Дистрибутив Miniconda доступен для Windows, macOS и Linux. Нужную
версию можно загрузить с официального сайта ( https : / / docs . conda . io /
en / latest / miniconda . html ) . В следующем примере мы запускаем сеанс в
Docker- контейнере на основе Ubuntu, загружая 64- разрядный инсталлятор
Linux через утилиту wget и устанавливая Miniconda. Данный пример ( возможно, с незначительными изменениями ) должен работать в любой системе
на основе Linux или macOS.
$ docker run -ti - h py4fi - p 11111:11111 ubuntu:latest / bin/ bash
root@py4fi:/# apt - get update; apt - get upgrade - y
•••

root@py4fi:/# apt - get install - y bzip2 gcc wget

root@py4fi:/# cd root
root@py4fi:~# wget \
> https://repo.continuum.io/ miniconda/Miniconda 3 -latest - Linux - x86 _64.sh \
> -0 miniconda.sh

HTTP request sent, awaiting response... 200 OK
Length: 62574861 (60M)[application/x - sh]
Saving to: ' miniconda.sh'
miniconda.sh

=================>] 59.68M

100%[

- -

2018 09 15 09:44:28 (5.42 MB/s)
[62574861/62574861]

-

5.97MB/s

in 11s

' miniconda.sh ' saved

root@py4fi:~# bash miniconda.sh
Инфраструктура Python

65

Welcome to Miniconda 3 4.5 .11
In order to continue the installation process, please review the
license agreement
Please, press ENTER to continue

.

»>

Чтобы начать процесс установки, нажмите клавишу < Enter >. Подтвердите
условия лицензионного соглашения, введя yes.
Do you accept the license terms ? [ yes | no ]
[ no ] »> yes

Miniconda 3 will now be installed into this location:
/ root / miniconda 3

-

Press ENTER to confirm the location
Press CTRL - C to abort the installation
Or specify a different location below

[ / root / miniconda 3 ] »>
PREFIX= / root / miniconda 3
installing: python - 3.7

. ...

.

_ ...
_ ...

installing : requests - 2.19 1- py 37 0
installing: conda - 4.5 Il- py 37 0
installation finished

.
.

Далее необходимо подтвердить путь установки и разрешить Miniconda добавить его в переменную среды РАТИ. Еще раз введите yes.
Do you wish the installer to prepend the Miniconda 3 install location
to PATH in your / root / bashrc ? [ yes |no ]
[ no ] »> yes

.

.

Appending source / root /miniconda3 / bin / activate to / root / bashrc
A backup will be made to: / root / bashrc - miniconda 3 bak

.

.

.

For this change to become active, you have to open a new terminal

Thank you for installing Miniconda 3 !
root @ py 4ft : ~ #

66

Глава 2

Теперь можно обновить утилиту conda и среду Python 3.
root @ py 4fi: ~# export PATH= " / root /miniconda 3 / bin / :$PATH"
root @ py 4fi : ~# conda update - y conda python

root @ py 4fi: ~# echo " . / root /miniconda 3 / etc /profile . d / conda . sh" »
~ / . bashrc
root @ py 4fi : ~# bash

Выполнив указанные действия, вы получите в свое распоряжение базовый
интерпретатор Python и менеджер пакетов conda . В базовый дистрибутив
Python включен ряд полезных библиотек, например СУБД SQLite3 (https : / /
sqlite org ). Попробуйте запустить Python , чтобы проверить, находит ли
система путь к интерпретатору.

.

root @ py 4fi : ~ # python
Python 3.7 0 ( default , Jun 28 2018 , 13: 15 : 42 )
[ GCC 7.2 0 ] :: Anaconda , Inc on linux
Type " help " , " copyright " , " credits " or " license " for more information .
»> print ( ' Hello Python for Finance World . ' )
Hello Python for Finance World .
»> exit()
root @ py 4fi : ~#

.

.

.

Выполнение основных команд в менеджере conda
Утилита conda применяется для установки, обновления и удаления любых
пакетов Python. Ниже приведен синтаксис основных команд.

Установка Python х.х
conda install python = x. x
Обновление Python
conda update python
Установка пакета
conda install $ ИМЯ ПАКЕТА

_

Обновление пакета
conda update % ИМЯ ПАКЕТА

_

Удаление пакета
conda remove $ ИМЯ ПАКЕТА

_

3

Пакет Miniconda обновляется намного реже, чем conda и Python.

Инфраструктура Python

67

Обновление самой утилиты conda
conda update conda
Поиск пакета

conda search %КРИТЕРИЙ_ ПОИСКА
Вывод списка установленных пакетов
conda list



К примеру, для установки NumPy
одной из важнейших библиотек на учного стека Python достаточно ввести одну-единственную команду. Если
библиотека устанавливается на компьютере, который оснащен процессором
Intel, то вместе с NumPy автоматически устанавливается математическая
библиотека mkl (https : / / docs continuun.io /mkl - optinizations / ), ускоряющая вычисления не только для NumPy, но и для многих других пакетов
научного стека4.



.

root @ py 4 fi: ~# conda install numpy
Solving environment : done
# # Package Plan ##

environment location:
added

-

/ root / miniconda3

/ updated specs :
numpy

The following packages will be downloaded:
package

|

build

.I

mkl - 2019.0
Intel - openmp - 2019.0
mkl_random - 1.0 1
libgfortran - ng - 7.3 0

.

.

.

numpy - 1.15 1
numpy - base - 1.15 1
blas - 1.0
mkl_ fft - 1.0 4

.

.

|
|
|
|
|
|
|

j

117
117
py 37h4414c 95 l
hdf 63c 60_0
py 37hld66e8 a 0
py 37h81de0dd_0

_
_

mkl

_

py 37h 4414c 95 l

Total:

4

204.4
721
372
1.3
37
4.2
6
149

MB
KB
KB
MB
KB
MB
KB
KB

211.1 MB

При установке метапакета nomkl с помощью команды conda install numpy nomkl библиотека mkl и связанные с ней пакеты не будут инсталлироваться автоматически.

68

Глава 2

The following NEW packages will be INSTALLED:
bias:
1.0 - mkl
intel openmp: 2019.0- 117
libgfortran - ng: 7.3.0- hdf63c60 0

-

_

nkl :

_

_

nkl fft:
mkl random:
numpy:
numpy - base:

-

2019.0 117
1.0.4 py37h4414c95 l
1.0.1 py37h4414c95 l
1.15.1 py37hld66e8a 0
1.15.1 py37h81de0dd 0

-

_

_

_

_

-

-

Proceed ([у]/ п)? у
Downloading and Extracting Packages
mkl 2019.0
| 204.4 MB i ############################

-

-

numpy 1.15.1
| 37 KB
numpy base 1.15.1 |4.2 MB

-

i
i

############################
############################

i

100%

i

100%

| 100%

root@py4fi:~#

За один раз можно установить сразу несколько пакетов. Флаг - у означает,
что на все возникающие вопросы следует отвечать yes.
root@py4fi:/# conda install - у ipython matplotlib pandas pytables \
scikit - learn sclpy

>

--

pytables 3.4.4
| 1.5 MB |#############################|100%
kiwisolver 1.0.1 |83 KB
| ############################# | 100%
icu 58.2
|22.5 MB | ############################# j 100%
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
root@py4fi:~#

-

После завершения инсталляции в системе станут доступны наиболее важные библиотеки, применяемые для финансового анализа. Рассмотрим их на значение.

IPython
Улучшенная интерактивная оболочка Python.
natplotlib
Стандартная библиотека визуализации данных в Python.
Инфраструктура Python

69

NumPy
Библиотека для работы с числовыми массивами.
pandas

Библиотека, предназначенная для управления табличными данными,
например временными рядами.

PyTables
Оболочка для работы с файлами формата HDF5 (http://hdfgroup.
org/).

Scikit -learn
Пакет инструментов машинного обучения.

SciPy
Библиотека классов и функций, применяемых в научных расчетах ( уста навливается в связке с другими библиотеками ).
В результате вы получите базовый набор инструментов анализа данных,
в том числе финансовых. В следующем примере показано, как с помощью
IPython получить список псевдослучайных чисел, сгенерированных библиотекой NumPy.
root@py4fi:~# ipython
Python 3.7.0(default, Jun 28 2018, 13:15:42)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.5.0
An enhanced Interactive Python. Type '?' for help.

--

In [1]: import numpy as np
In [2]: np.random.seed(10O)

_

In [3]: np.random.standard normal((5, 4))
0ut[3]:
array([[ 1.74976547, 0.3426804 , 1.1530358 ,
[ 0.98132079, 0.51421884, 0.22117967,
[ 0.18949583, 0.25500144, 0.45802699,
[ 0.58359505, 0.81684707, 0.67272081,
[ 0.53128038, 1.02973269, 0.43813562,

-

In [4]: exit
root@py4fi:~#

70

Глава 2

-

-0.25243604],

1.07004333],
-0.43516349
],

-0.10441114],
- 1.11831825]])

С помощью команды conda list можно получить список установленных

пакетов.

root@py4fi:~# conda list
in environment at /root/miniconda3:

# packages
#
# Name

Build Channel

Version

bzip2

0.24.0
0.1.0
1.0
1.14.4
1.0.6

python

3.7.0

_
_
mkl
hdbcaa40_0
hl4c3975_5
hc3d631a_0

wheel
xz
yaml
zlib
root@py4fi:~#

0.31.1
5.2.4
0.1.7
1.2.11

py37
hl4c3975
had09818
ha838bed

asnlcrypto

backcall
bias
blosc

py37 0
py37 0

__4
0

_2
_2

Если пакет больше не нужен,удалите егос помощью команды conda remove.
root@py4fi:~# conda remove scikit -learn
Solving environment: done
## Package

Plan

##

environment location: /root/miniconda3
removed specs:
scikit learn

-

-

The following packages will be REMOVED:

_

scikit - learn: 0.19.1- py37hedc74O6 0

Proceed ([у]/п)? у
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
root@py4fi:~#

Инфраструктура Python

71

Преимущества утилиты conda как менеджера пакетов вполне очевидны.
Но в полной мере ее возможности раскрываются при использовании в качестве менеджера виртуального окружения.

ш

Простое управление пакетами
Использование утилиты conda в качестве менеджера пакетов упрощает задачи установки, обновления и удаления пакетов Python.
Вам не придется выполнять самостоятельную сборку и компиляцию пакетов, что сопряжено с целым рядом трудностей, связанных
с наличием большого количества зависимостей и нюансов различных операционных систем.

conda как менеджер виртуального окружения
В зависимости от выбранной версии инсталлятора Miniconda устанавлива ет либо Python 2.7, либо Python 3.7. При этом менеджер conda позволяет уста новить в системе сразу обе среды: например, к стандартной версии Python 3.7
добавить отдельную инсталляцию Python 2.7.x. Для этого в утилите conda
имеются следующие команды.

Создание виртуальной среды
conda create - - name $ ИМЯ СРЕДЫ

_

Подключение виртуальной среды
conda activate $ ИМЯ СРЕДЫ

_

Отключение виртуальной среды
conda deactivate $ ИМЯ СРЕДЫ

_

Удаление виртуальной среды
conda env remove - - name % ИМЯ СРЕДЫ

_

Экспорт виртуальной среды в файл
conda env export > $ ИМЯ ФАЙЛА

_

Создание виртуальной среды из файла
conda env create - f $ ИМЯ ФАЙЛА

_

Вывод списка всех имеющихся виртуальных сред
conda info - - envs
В качестве примера создадим виртуальную среду с именем ру 27, установим
в ней IPython и выполним строку кода Python 2.7.

72

Глава 2

root @ py 4 fi: ~ # conda create - - name py 27 python=2.7
Solving environment : done
## Package Plan # #

environment location:

/ root / miniconda3 / envs / py 27

added / updated specs :
- python=2.7

The following NEW packages will be INSTALLED:

.

ca - certificates : 2018.03 07 - 0

python:

zlib :

_
1.2.11- ha 838 bed_ 2
.

2.7 15 - hl571d 57 0

Proceed ( [ у ] / п) ? у
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
#
#
#
#
#
#
#

To activate this environment , use :
conda activate py 27

>

To deactivate an active environment , use :
>

conda deactivate

root @ py 4 fi: ~#

Обратите внимание на то, что после подключения виртуальной среды при глашение командной строки будет содержать ее имя ( ру 27 ).
root @ py 4 fi:~# conda activate ру 27
( ру 27 ) root @py 4 fi: ~ # conda install ipython
Solving environment : done

Executing transaction: done
( py 27 ) root @py 4fi: ~#

Инфраструктура Python

73

Теперь в IPython можно выполнять команды Python 2.7.

(ру27) root@py4fi:~# ipython
Python 2.7.15 [ Anaconda, Inc.|(default, May 1 2018, 23:32:55)
Type "copyright", "credits" or "license" for more information.
IPython 5.8.0
>
?
%quickref >
help
>
>
object?

-

--

An enhanced Interactive Python.
Introduction and overview of IPython's features.

Quick reference.
Python's own help system.
Details about 'object' , use 'object??' for extra details.

In [1]: print "Hello Python for Finance World!"
Hello Python for Finance World!
In [2]: exit
(py27) root@py4fi:~#
Этот пример показывает, что утилита conda позволяет установить в одной
системе несколько версий Python. Также можно устанавливать разные версии
определенных пакетов. Добавление еще одной среды никоим образом не вли яет на существующую среду Python и другие среды , которые были установле ны ранее . Список всех имеющихся сред можно вывести с помощью команды

conda env list .
(ру27) root@py4fi:~# conda env list
# conda environments:
#

base

/root/miniconda3
* /root/miniconda3/envs/py27

py27

(py27) root@py4fi:~#
Иногда требуется поделиться информацией о среде с другими пользователями или запустить одну и ту же среду на нескольких компьютерах. Для этого следует экспортировать список установленных пакетов в файл с помощью
команды conda env export . По умолчанию перенос среды возможен только
на компьютер с аналогичной операционной системой, поскольку в результи рующем файле YAML могут быть указаны не версии сборок , а только версии
пакетов .

(ру27) root@py4fi:~# conda env export
(py27) root@py4fi:~# cat py27env.yml
name: py27

74

Глава 2

- - no- builds

>

py27env.yml

channels :
- defaults
dependencies :

-

backports =1.0

-

python = 2.7 15

-

zlib=1.2 .11
/ root / niniconda 3 / envs / py 27

.

prefix :

( py 27 ) root @ py 4fi : ~#

Зачастую виртуальную среду, которая с технической точки зрения представляет собой всего лишь структуру подкаталогов, создают для выполнения
быстрых тестов5. В таком случае ее можно легко удалить сразу же после деак тивации с помощью команды conda env remove.
( ру 27 ) root @ py 4fi : / # conda deactivate
root @ py 4fi : ~ # conda env remove - y - - name py 27

Remove all packages in environment

/ root / miniconda 3 / envs / py 27:

## Package Plan ##

environment location :

/ root / miniconda 3 / envs / py 27

The following packages will be REMOVED :

_

backports :

1.0 - py 27 l

zlib :

1.2 .11 - ha 838 bed 2

_

root @ py 4fi : ~ #

На этом краткий обзор возможностей менеджера виртуального окружения
conda можно считать завершенным.

5

В официальной документации (https : / / packaging . python . org / tutorials / installing packages / #creating - virtual - environments ) сказано следующее: “ Виртуальная среда
Python позволяет устанавливать пакеты в изолированном каталоге для конкретного приложения, а не для всего дистрибутива”

Инфраструктура Python

75

Простое управление виртуальным окружением
Утилита conda не только позволяет управлять пакетами; она
также представляет собой менеджер виртуального окружения для
|Python. Утилита упрощает создание различных дистрибутивов
т
Python, позволяя иметь разные версии языка с разным набором
пакетов на одном и том же компьютере, причем они никак не будут
влиять друг на друга. Кроме того, с помощью утилиты conda можно экспортировать виртуальную среду в файл, чтобы перенести на
другой компьютер либо поделиться с другими пользователями.

<

-

Контейнеры Docker
Контейнеры Docker за последние годы произвели настоящий фурор в
IT-среде. Несмотря на относительную новизну, технология прекрасно зарекомендовала себя в качестве стандартной платформы для эффективного развертывания практически любых типов приложений.
В данной книге контейнер Docker рассматривается как обособленная
файловая система, включающая операционную систему ( например, Ubuntu
Server 18.04), среду Python, вспомогательные системные утилиты, а также все
необходимые программные библиотеки и пакеты. Такой контейнер можно запустить как на локальном компьютере с Windows 10, так и в облачном экземпляре с операционной системой Linux.
В этом разделе не приводится полное описание Docker. Мы лишь вкратце
поговорим о том, что это за технология и что она позволяет делать в контексте
развертывания среды Python6.

Контейнеры и образы
Прежде чем двигаться дальше, необходимо уяснить разницу между двумя
ключевыми концепциями Docker. Образ Docker можно сравнить с классом
Python. В свою очередь, контейнер Docker можно уподобить экземпляру соответствующего класса7.
Техническое определение понятия “ образ” дано в словаре Docker ( https : / /
docs . docker . com / glossary / ) .



Образы Docker это основа для контейнеров. Образ представляет собой упорядоченную коллекцию сведений об изменениях в корневой файловой системе
6

7

Подробное введение в технологию Docker дано у Маттиаса и Кейна [ 1].
Терминология объектно- ориентированного программирования рассматривается в главе 6.

76

Глава 2

и параметрах выполнения программ, запускаемых из контейнера. Образ обычно содержит группу файловых систем с каскадно- объединенным монтированием. Базовый образ не имеет состояния и никогда не меняется.

Там же можно найти определение понятия “ контейнер”, благодаря которо му аналогия с классами и объектами Python становится очевидной.
Контейнер — это исполняемый экземпляр образа Docker. Он содержит сам
образ, исполняющую среду и стандартный набор инструкций.

Установка программного обеспечения Docker зависит от операционной сис темы. Именно поэтому мы не будем детально ее рассматривать. Дополнитель ные сведения можно найти на сайте https : / / docs docker con / get - docker / .

.

.

Создание образа Docker с Ubuntu и Python
В этом разделе описывается процедура создания образа Docker на осно ве последней версии операционной системы Ubuntu, в которой установлены Miniconda и ряд важных пакетов Python. При этом контейнер обновляет список пакетов Linux, обновляет сами пакеты в случае необходимости и
устанавливает дополнительные системные утилиты. Для этого применяются
два сценария Первый запускается из командной оболочки bash и отвечает за
конфигурирование среды Linux8. Второй сценарий называется Dockerfile — он
управляет процедурой сборки самого образа.
Установочный fras/z - сценарий, приведенный в листинге 2.1, состоит из трех
разделов В первом разделе выполняется конфигурирование среды Linux, во
втором — устанавливается Miniconda, в третьем — инсталлируются дополнительные пакеты Python. Остальные детали описаны в комментариях к файлу.

.

.

Листинг 2.1. Сценарий установки Python и дополнительных пакетов
( installsh )
#1 / bin / bash
#
# Сценарий установки системных
# утилит Linux и основных
# пакетов Python
#
# Python for Finance , 2nd ed .
# ( c ) Dr Yves J Hilpisch

.

8

.

Описание возможностей оболочки bash см. у Роббинса [ 2 ]. Посетите также сайт https : / /
www gnu org / software / bash.

. .

Инфраструктура Python

77

#
# КОНФИГУРИРОВАНИЕ LINUX
apt - get update
U обновление списка пакетов
apt get upgrade - у
# обновление самих пакетов
# Установка системных утилит
apt get install - у bzip 2 gcc git htop screen vim wget
apt - get upgrade - y bash
# обновление bash
# очистка списка пакетов
apt - get clean

-

U УСТАНОВКА ИINICONDA
# Загрузка Miniconda
wget https: / / repo continuum io / miniconda /
Miniconda3 - latest Linux x 86 64 sh 0 Miniconda sh

.

.

-

.

-

_ .

-

.

# установка
bash Miniconda sh - b
# удаление инсталлятора
rm - rf Miniconda sh
export PATH= " / root / miniconda 3 / bin: $ PATH " # добавление нового пути

.

# УСТАНОВКА БИБЛИОТЕК PYTHON
conda update у conda python

-

conda install
conda install

# обновление утилиты Conda и Python

-y

# установка библиотеки Pandas
# установка оболочки IPython

pandas
- y ipython

Сценарий Dockerfile, приведенный в листинге 2.2, использует bash -c денарий из листинга 2.1 для сборки нового образа Docker. Выполняемые действия
также описаны в комментариях.
Листинг 2.2. Сценарий сборки образа ( Dockerfile )
#
А Создание образа Docker , включающего последнюю версию
# операционной системы Ubuntu и базовый дистрибутив Python

U
Л Python for Finance , 2nd ed
# ( c ) Dr . Yves J H i l p i s c h
#

.

.

# Использование последней версии Ubuntu
FROM ubuntu:latest

# Сведения об установщике
MAINTAINER yves

# Подключение bash - сценария
ADD install sh /

.

78

Глава 2

# Изменение прав доспупо к сценарию
RUN chmod и+х /install sh

.

# Выполнение bash - сценария
RUN / install sh

.

# Добавление нового пути
ENV PATH / root /miniconda 3 / bin: $PATH
tt Запуск оболочки IPython при выполнении контейнера
CMD [ " ipython " ]

Если оба этих файла находятся в одной папке, а в системе установлено
программное обеспечение Docker, то процесс сборки нового образа Docker
предельно упрощается. В данном примере образ помечается дескриптором
py 4fi:basic. Его нужно указывать в качестве ссылки на образ при запуске
соответствующего контейнера.
~ / Docker $ docker build - t py4fi:basic

.

Removing intermediate container 5 fec 0c 9b2239
- - - > acceel28d9e9
Step 6 / 7 : ENV PATH / root / miniconda 3 / bin: $PATH
- - - > Running in a 2bb97686255
Removing intermediate container a 2bb 97686255
- - - > 73b00c 215351
Step 7 / 7 : CMD [ " ipython " ]
- - - > Running in ec 7 acd90c 991
Removing intermediate container ec 7 acd90c 991
- - - > 6c 36b9117cd 2
Successfully built 6 c 36b 9117cd2
Successfully tagged py 4fi: basic
~ / Docker $

Получить список доступных образов Docker можно с помощью команды
docker images. Последний созданный образ указывается в списке первым.
( py 4 fi) ~ / Docker $ docker images
REPOSITORY
TAG
IMAGE ID
py 4 fi
basic
f 789 dd 230d 6 f
ubuntu
latest
cd6d8154 flel
( py 4 fi) ~ / Docker $

CREATED
About a minute ago
9 days ago

SIZE

1.79GB
84.1MB

Инфраструктура Python

79

После успешной сборки образа py4fi:basic запустить соответствующий
контейнер Docker можно с помощью команды docker run. Параметр - ti
позволяет запустить в контейнере интерактивные процессы, например командную оболочку ( см. https://docs.docker.con/engine/reference/run/).

-

~/Docker$ docker run ti py4fi:basic
Python 3.7.0(default, Jun 28 2018, 13:15:42)
Type ' copyright ' , 'credits' or 'license' for more information
IPython 6.5.0
An enhanced Interactive Python. Type '?' for help.

--

In [1]: import numpy as np

In [2]: a

= np.random.standard _normal((5, 3))

In [3]: import pandas as pd
In [4]: df

=

pd.DataFrame(a, columns=[' a', ' b ' , 'c'])

In [5]: df
0ut[5]:

a
0
1
2
3
4

-

-

1.412661
1.294977
1.156361
0.546736
1.972943

-

b

c

0.881592
0.546676
1.979057
0.479821
0.193964

1.704623
1.027046
0.989772
0.693907
0.769500

In [6]:
Выход из оболочки IPython приведет к закрытию контейнера, так как это
единственное запущенное в нем приложение. Если же нужно выйти из кон тейнера, не завершая его работу, то нажмите комбинацию клавиш < Ctrl + P > и
< Ctrl + Q >.
Выполнив команду docker ps, мы увидим, что контейнер остается запущенным ( равно как и другие запущенные ранее контейнеры ).
~/Docker$ docker ps
CONTAINER ID IMAGE
e815df8f0f4d py4fi:basic
4518917de7dc ubuntu.’latest
d081b5c7add0 ubuntu:latest

80

Глава 2

COMMAND
"ipython"
"/ bin/bash "
"/ bin/bash "

CREATED

\
About a minute ago \
About an hour ago \
21 hours ago
\

STATUS
Up About a minute
Up About an hour
Up 21 hours

~ / Docker $
Подключение контейнера Docker осуществляется командой docker attach
$10, причем в качестве идентификатора достаточно указать первые несколько
символов.

~ / Docker $ docker attach e815d

.

In [ 6 ] : df infoQ
1
pandas core frame . DataFrame ' >
Rangelndex : 5 entries , 0 to 4
Data columns ( total 3 columns ) :
5 non null float 64
a
b
5 non - null float 64
c
5 non - null float 64
dtypes : float 64 ( 3 )
memory usage : 200.0 bytes
< class

.

.

-

In [ 7 ] : exit
~ / Docker $

Команда exit завершает работу IPython и вызывает закрытие контейнера
Docker. Контейнер можно удалить с помощью команды docker rm.

~ / Docker $ docker rm e815d
e815d
~ / Docker $
Аналогичным образом можно удалить сам образ py 4fi : basic с помощью
команды docker rmi. Контейнеры достаточно компактны, но образы могут
занимать много места на диске. Например, образ py 4fi : basic имеет размер
около 2 Гбайт. Вот почему необходимо регулярно очищать список образов

Docker.
~ / Docker $ docker rmi 6c 36 b9117cd 2
О контейнерах Docker можно говорить еще очень долго, но читателям кни ги достаточно знать, что контейнеры представляют собой современную технологию развертывания полностью изолированной среды Python и с их помощью можно распространять приложения алгоритмической торговли.

Инфраструктура Python

81

(
I

Преимущества контейнеров Docker
Если вам еще не доводилось работать с контейнерами Docker, то
рекомендуем познакомиться с ними поближе. Они имеют целый
ряд преимуществ, связанных с развертыванием среды Python, которые проявляются не столько в локальном окружении, сколько
при работе с облачными экземплярами и серверами алгоритмической торговли.

Облачные экземпляры
В этом разделе будет показано, как сконфигурировать полноценную ин фраструктуру Python на облаке DigitalOcean ( www . digitalocean . com ) . Существует множество других облачных решений, включая AWS ( Amazon Web
Services; https : / / aws . amazon . com / ru / ) . Однако DigitalOcean славится своей
простотой и относительной дешевизной небольших облачных экземпляров,
называемых дроплетами. Наименьший дроплет, которого обычно достаточно
для задач интерактивной разработки, обойдется всего в 5 долл, в месяц или
0,7 цента в час. Оплата взимается на почасовой основе, что позволяет создать
дроплет буквально на пару часов, выполнить в нем все необходимые действия
и удалить, заплатив 1,5 цента9.
Нашей целью будет создание собственного дроплета на облаке DigitalOcean,
в котором мы установим Python 3.7, ключевые пакеты ( NumPy, pandas и пр.)
и сервер Jupyter Notebook ( https : / / jupyter . org / ) с парольным доступом,
снабженный SSL- шифрованием. Сервер будет содержать три базовых компонента, доступ к которым можно получить через обычный браузер.

Jupyter Notebook
Популярная интерактивная среда разработки, поддерживающая несколько языков программирования ( в частности, Python, R и Julia).

Терминал
Командная оболочка, запускаемая из браузера и позволяющая выпол нять типичные задачи системного администрирования, а также пользоваться утилитами типа Vim или git .

Редактор
Запускаемый из браузера редактор кода с цветовой разметкой синтакси са, поддержкой различных языков программирования и типов файлов.
9

_ _

Новые пользователи, которые зарегистрируются по ссылке https: / / bit . ly / do sign up, получат 60 -дневный приветственный бонус в размере 100 долларов.

82

Глава 2

Установка Jupyter Notebook на дроплете позволяет развернуть среду разработки в браузере, избегая необходимости регистрироваться в облачном эк земпляре через SSL-соединение.
Для решения поставленных задач нам понадобится несколько файлов.

Сценарий настройки сервера
Управляет всеми этапами, в частности копирует все остальные файлы в
дроплет и запускает их на выполнение.

Сценарий инсталляции Python и Jupyter Notebook
Устанавливает Python, дополнительные пакеты и среду Jupyter Notebook,
а также запускает сервер Jupyter Notebook.
Файл конфигурирования Jupyter Notebook

Настраивает сервер Jupyter Notebook, в частности подключая парольный доступ.
Файлы открытого и закрытого ключей RSA
Требуются для SSL- шифрования на сервере Jupyter Notebook.
Мы пройдем этот список в обратном порядке.

Открытый и закрытый ключи RSA
Чтобы иметь возможность создавать защищенное подключение к серверу
Jupyter Notebook из любого браузера, нам нужен SSL-сертификат, состоящий
из открытого и закрытого ключей RSA (https : / / ru . wikipedia . org / wiki /
RSA ). Как правило, такой сертификат выдается одним из центров сертифика ции ( Certificate Authority СА), но для задач книги будет достаточно самостоятельно сгенерированного сертификата 10. Пары RSA- ключей можно сгенерировать с помощью утилиты OpenSSL ( www . openssl . org ). Ниже показано,
как сгенерировать сертификат для доступа к серверу Jupyter Notebook ( запол няйте предлагаемые поля собственными значениями ) .



-- -

-

-

~ / cloud $ openssl req х509 nodes days 365 newkey \
rsa:1024 out cert.pen keyout cert.key

-

Generating a 1024 bit RSA private key

.. +++ +++

++++++

.

writing new private key to ' cert key '

10

При работе с таким сертификатом понадобится добавить исключение в настройки безопасности браузера.

Инфраструктура Python

83

Далее вас попросят ввести информацию, включаемую в сертификат. Вам
обязательно нужно будет указать уникальное имя ( Distinguished Name DN ).
Можно оставить некоторые из полей незаполненными или принять значения,
заданные по умолчанию. Поле останется незаполненным, если в качестве зна чения ввести точку ( . ).



Country Name ( 2 l e t t e r code ) [ AU ] : DE
State or Province Name ( full name ) [ Some - State ]:Saarland
Locality Name ( eg , c i t y ) [ ]:Voelklingen
Organization Name ( eg , company ) [ Internet Widgits Pty Ltd ] : TPQ GmbH
Organizational Unit Name ( eg , section ) []:Python for Finance
Common Name ( e . g server FQDN or YOUR name ) [ ] :Jupyter
Email Address [ ]:team@tpq.io

.

~ / cloud $ Is
cert . key
~ / cloud $

cert . pern

Файлы cert.key и cert.pern нужно скопировать в дроплет, добавив ссылку на них
в конфигурационный файл Jupyter Notebook, который рассматривается далее.

Конфигурационный файл Jupyter Notebook
Общедоступный сервер Jupyter Notebook можно развернуть в безопасной
среде согласно инструкциям, приведенным в официальной документации
( http : / / bit . ly / 2 Ka 0 t f I ). В частности, доступ к серверу можно защитить
паролем. Для этого следует воспользоваться функцией passwdQ из пакета
notebook . auth, которая генерирует хеш - код пароля. В приведенном ниже
примере в качестве пароля используется слово “ jupyter”.
~ / cloud $ ipython

.

Python 3.7 0 ( default , Jun 28 2018 , 13 : 15 : 42 )
Type ' copyright ' , ' credits ' or ' license ' for more information
IPython 6.5 . 0 - - An enhanced Interactive Python Type ' ? ' for help .

.

In [1] : from notebook . auth import passwd
In [ 2 ] : passwd ( ' jupyter ' )
Out[2]: 'Shal:d4d34232ac3a:55ea0ffd78cc3299e3e5e6ecc0d 36be0935d424b'
In [ 3 ] : exit

Полученный хеш - код следует включить в конфигурационный файл сервера
Jupyter Notebook ( листинг 2.3) . Предполагается, что файлы RSA- ключей находятся в папке / root/ . jupyter/ дроплета.
84

Глава 2

Пример 2.3. Файл конфигурации Jupyter Notebook ( jupyter _notebook _config.py )
# Конфигурационный файл Jupyter Notebook

Python

.

for Finance , 2nd ed .

.

# ( c ) Dr Yves J H i l p i s c h
#
# SSL - ШИФРОВАНИЕ
# Замените пути ( и сами файлы ) своими вариантами
c . NotebookApp . certfile = u ' / root / . jupyter / cert . pem '
с . NotebookApp . keyflie = и ' / r o o t / jupyter / cert . key '

.

# IP - АДРЕС И ПОРТ
# Чтобы разрешить все IP - адреса облачного
1
# экземпляра , задайте 1 *

c . NotebookApp . ip =
# Имеет смысл использовать фиксированный
# номер порта для доступа к серверу
c . NotebookApp . port = 8888

# ЗАЩИТА ПАРОЛЕМ
1
# Пароль : ' j u p y t e r
# Замените хеш - кодом своего , более надежного пароля
с . NotebookApp . password = \
' shal : d 4d 34232ac 3 a : 55 ea 0 ffd78 cc 3299 e 3 e 5e6ecc 0d 36 be0935 d 424 b 1
# БЛОКИРОВКА ЗАПУСКА БРАУЗЕРА
# Предотвращение запуска браузера из Jupyter

с . NotebookApp . open_ browser = False

Jupyter Notebook и безопасность
Развертывание Jupyter Notebook в облаке вызывает целый ряд проблем с безопасностью, ведь это полнофункциональная среда разработки, запускаемая из браузера. Следовательно, крайне важно
использовать все предусмотренные в Jupyter Notebook средства
защиты, включая пароль и SSL- шифрование. Но это только начальный уровень. В зависимости от того, что конкретно делается в
облачном экземпляре, могут понадобиться дополнительные меры
безопасности.

Следующий шаг



установка Python и Jupyter Notebook в дроплете.

Инфраструктура Python

85

Сценарий установки Python и Jupyter Notebook
Сценарий установки Python и Jupyter Notebook напоминает bash - сценарий,
который рассматривался в разделе “ Контейнеры Docker” и предназначался
для инсталляции Python через установщик Miniconda в контейнере Docker.
Однако сценарий, приведенный в листинге 2.4, требует запуска сервера Jupyter
Notebook. Как и прежде, все выполняемые действия описаны в комментариях.

.

Листинг 2 А Сценарий установки Python и запуска сервера Jupyter Notebook
( install.sh )
# ! / bin / bash

#
# Сценарий установки системных утилит
# Linux 3 основных пакетов Python
# и сервера Jupyter Notebook
#

Python for Finance , 2nd ed ,
# ( c ) Dr , Yves J . Hilpisch
U
# КОНФИГУРИРОВАНИЕ LINUX
# обновление списка пакетов
apt - get update
# обновление самих пакетов
apt - get upgrade - у
# Установка системных утилит
apt - get install - у bzip 2 gcc git htop screen vim wget
apt - get upgrade - y bash
У обновление bash
apt - get clean
# очистка списка пакетов
U

У УСТАНОВКА MINICONDA

.

_ .

.

wget https: / / repo continuum io /miniconda / Miniconda 3 - latest - Linux - x86 64 sh
- 0 Miniconda sh
bash Miniconda sh b
# установка
# удаление инсталлятора
rm Miniconda sh
# Добавление нового пути для текущего сеанса
export PATH= " / root / miniconda 3 / bin: $ РАТН "
# Добавление нового пути в настройках оболочки
echo " / root / miniconda 3 / etc / profile d / conda sh " » / bashrc

.

.

.

-

.

echo " conda activate " »

-/ .bashrc

.

.

-.

U УСТАНОВКА БИБЛИОТЕК PYTHON
У В зависимости от решаемых задач могут
# понадобиться дополнительные пакеты
conda update - у conda
# обновление утилиты conda
conda create у - n py 4fi python=3.7 # создание среды

-

86

Глава 2

\

source activate py 4fi
conda install - y jupyter

conda
conda
conda
conda

install
install
install
install

-y

#
#
#
#
#

pytables
pandas
matplotlib

U

- y scikit - learn

#

-y
-y

conda install - y openpyxl
conda install - y pyyaml

#
#
ft

запуск новой среды
интерактивная разработка
в браузере
оболочка для работы с файлами HDFS
пакет анализа данных
библиотека визуализации данных
библиотека инструментов
машинного обучения
библиотека обмена данными с Excel
библиотека обработки файлов YAML

# обновление менеджера пакетов
pip install - - upgrade pip
# визуализация данных из библиотеки pandas
pip install cufflinks
# КОПИРОВАНИЕ ФАЙЛОВ И СОЗДАНИЕ КАТАЛОГОВ

.

/ root / jupyter
mv / root / jupyter notebook config py
mv / root / cert * / root / jupyter
mkdir / root / notebook
cd / root / notebook
mkdir

.

_

_

.

.

/ root / . jupyter /

# ЗАПУСК СЕРВЕРА JUPYTER NOTEBOOK
jupyter notebook - allow root

-

-

# Запуск сервера в фоновом режиме :

# jupyter notebook - - a l l o w - r o o t &

Этот сценарий нужно скопировать в дроплет и запустить из сценария
оркестровки, описанного в следующем разделе.

Сценарий оркестровки для процесса установки дроплета
Следующий bash - сценарий ( листинг 2.5) самый короткий. Онотвечает
за настройку дроплета, копируя в него все остальные файлы, при этом IPадрес дроплета служит параметром сценария. В последней строке запускается
сценарий install.sh, который в свою очередь инсталлирует и запускает сервер
Jupyter Notebook.
Листинг 2.5. Сценарий оркестровки дроплета (setup.sh )

/ b i n/ b a s h
#
# Настройка дроплета D i g i t a l O c e a n ,
Л содержащего базовый стек пакетов Python
# и сервер Jupyter Notebook

Инфраструктура Python

87

#
# Python for F i n a n c e 2 n d ed
# ( c ) Dr Yves 3 H i l p i s c h
#

.

# ПОЛУЧЕНИЕ IP - АДРЕСА ИЗ КОМАНДНОЙ СТРОКИ
MASTER _IP=$1

# КОПИРОВАНИЕ ФАЙЛОВ
scp install sh root @ ${MASTER _IP } :
scp cert * jupyter notebook _conftg py root @ ${MASTER _IP } :

.

.

_

.

П ЗАПУСК СЦЕНАРИЯ ИНСТАЛЛЯЦИИ
ssh root @ $ {MASTER _IP} bash / root /install sh

.

Теперь можно приступать к установке. Создайте новый дроплет на сайте
DigitalOcean, указав для него следующие параметры.

Операционная система
Ubuntu 18.10 х64 ( последняя версия на момент подготовки книги).
Аппаратная конфигурация
Одно ядро, 1 Гбайт ОЗУ, 25 Гбайт SSD ( наименьший доступный дроплет ).
Регион дата- центра
Франкфурт ( поскольку автор книги проживает в Германии ).

SSH - ключ
Добавьте новый SSH - ключ для беспарольной аутентификации 11.
Имя дроплета
Можете использовать предложенное имя или сокращение типа py 4fi .

Процесс создания дроплета запускается после щелчка на кнопке Create
и длится около минуты. После этого вам будет выдан IP-адрес, например
46.101.156.199, если в качестве местонахождения дата - центра был указан
Франкфурт. Конфигурирование такого дроплета выполняется с помощью од ной команды:
( руЗ ) ~ / cloud$ bash setup . sh 46.101 . 156.199

Настройка длится несколько минут и завершается получением сообщения
от сервера Jupyter Notebook.
11

Подробно процедура описана на сайте DigitalOcean (https: / / do . co / 2 DIqnH9 и https : / /

.

do co / 2 A0 EALO ).

88

Глава 2

The Jupyter Notebook is running at :
https : I / [ в с е ip - адреса системы ] : 8888 /

Для получения доступа к серверу достаточно ввести в строке браузера следующий адрес (обратите внимание на префикс https : / / ):
https : / / 46.101 . 156.199 : 8888

Скорее всего, браузер спросит, хотите ли вы добавить исключение в на стройки безопасности, после чего появится форма ввода пароля (в данном
“ jupyter” ). Теперь вы готовы начать разрабатывать прилослучае пароль
жения Python в браузерной среде Jupyter Notebook. Вам также становится
доступной среда IPython через окно терминала и текстовый редактор. Кроме
того, из браузера можно управлять рабочими файлами: загружать, выгружать
на сервер, удалять и т.п.



а

Преимущества облачных решений
Комбинация технологий DigitalOcean и Jupyter Notebook предоставляет в распоряжение разработчиков Python и финансовых
аналитиков профессиональную вычислительную инфраструктуру.
Поставщики облачных услуг и дата - центры гарантируют физическую безопасность и непрерывную доступность виртуальных ком пьютеров. Это также сокращает стоимость этапа разработки, поскольку плата обычно взимается на почасовой основе, и не нужно
заключать никаких долгосрочных соглашений.

Резюме



Python ключевой язык программирования и технологическая платформа не только для данной книги, но и для большинства ведущих финансовых
компаний. В то же время развертывание среды Python может оказаться не простой и весьма трудоемкой задачей. К счастью, в последние годы появился
ряд эффективных технологий, помогающих справиться с возникающими про блемами. В частности, открытый пакет conda служит как менеджер пакетов
Python и одновременно как менеджер виртуального окружения. Контейнеры
Docker представляют собой следующий шаг, позволяя легко создать полноценную файловую систему и среду выполнения в изолированной “ песочнице”.
Следующий шаг вперед делают облачные провайдеры типа DigitalOcean, пред лагая быструю аренду вычислительных мощностей в профессионально управ ляемых и надежно защищенных дата - центрах с почасовой оплатой. Наличие
облачного экземпляра с развернутым на нем дистрибутивом Python 3.7 и за щищенным сервером Jupyter Notebook предоставляет в ваше распоряжение
Инфраструктура Python

89

профессиональную среду разработки приложений Python, в которой можно
создавать проекты для работы с финансовыми данными.

Дополнительные ресурсы
Ответы на вопросы, связанные с управлением пакетами Python , можно получить по следующим адресам:
• страница менеджера пакетов pip ( https : / / pypi . огд / project / pip / );

• страница менеджера пакетов conda ( https : // conda . io / en / latest / );
• инструкции по установке пакетов Python (https : / / packaging . python .
org / tutorials / installing - packages / ).
Информацию об управлении виртуальным окружением можно найти на
следующих сайтах:

.



сайт менеджера виртуального окружения virtualenv (https : // pypi
org / project / virtualenv / );



сайт менеджера виртуального окружения conda ( https : // conda . io /
projects / conda / en / latest / user - guide / concepts / environments . html );

• репозиторий менеджера пакетов и виртуального окружения
(https : / / github . com / pypa / pipenv ) .

pipenv

Подробные сведения о технологии Docker доступны на официальном сайте
( www docker com ).
О создании защищенного общедоступного сервера Jupyter Notebook рассказано в документации:

.

.

_

.

https : / / jupyter - notebook . readthedocs . io / en / latest / public server html

Одновременно управлять несколькими пользователями сервера Jupyter
Notebook можно через специальный хаб:

.

https : / / jupyterhub readthedocs . io / en / stable /

Чтобы подписаться на службу DigitalOcean, посетите страницу http: // bit .
ly / do sign up. Вы получите приветственный бонус в размере 100 долларов,
которого хватит на два месяца управления самым маленьким из дроплетов.
В главе упоминались следующие источники.

_

_

.
2. Роббинс, Арнольд. Bash. Карманный справочник, 2- е издание ( 2017,

1 Matthias, Karl, and Sean Kane. Docker: Up and Running ( 2015, O’Reilly) .

Вильямс).
90

Глава 2

НАСТЬ II

Основы Python

Эта часть книги посвящена основам программирования на Python. Приведенная здесь информация фундаментальна для понимания всех последующих
глав книги.
Главы данной части посвящены конкретным темам и послужат справоч ным руководством, к которому читатели смогут обращаться в случае необходимости.

• Глава 3 посвящена типам данных и структурам Python.
• Глава 4 посвящена библиотеке NumPy и ее классу nda ггау .
• Глава 5 посвящена библиотеке pandas и ее классу DataFrame .
• Глава 6 посвящена объектно-ориентрованному программированию на
Python.

ГЛАВА 3

Типы данных и структуры Python
Плохие программисты беспокоятся о коде. Хорошие программисты беспокоятся
о структурах данных и их взаимосвязях.

Линус Торвалъдс

В главе рассматриваются следующие темы.
Основные типы данных
В этом разделе описаны такие базовые типы данных, как int , float ,
bool и str .
Основные структуры данных
Из этого раздела вы узнаете о фундаментальных структурах Python ( таких, как объекты list ) . Кроме того, здесь рассматриваются управляющие конструкции, принципы функционального программирования и
анонимные функции .



Цель главы
дать общую информацию о существующих в Python типах
данных и структурах. Читатели, знакомые с другими языками программирования , такими как С или Matlab, легко поймут, какие особенности характерны
для Python. Приемы программирования, которые будут описаны в этой главе,
важны для понимания последующих глав.
В главе рассматриваются следующие типы данных и структуры.
Тип данных

Описание

Пример

int

Целочисленное значение

Натуральные числа

float

Число с плавающей точкой

Вещественные числа
Истина или ложь
Символ , слово, текст
Фиксированный набор объектов, кортеж
Настраиваемый список объектов
Словарь, индексируемый с помощью ключей
Набор уникальных объектов

bool

Булево значение

str

Строковый объект

tuple

list
diet

Неизменяемая последовательность
Изменяемая последовательность
Изменяемая последовательность

set

Изменяемая последовательность

Основные типы данных



язык с динамической типизацией . Это означает, что интерпреPython
татор определяет тип объекта на этапе выполнения программы. Компилируемые языки наподобие С обычно имеют статическую типизацию, при которой тип объекта задается заранее1.

Целые числа
Простейший тип данных



целое число, или int.

In [ l ] : а = 1 0
type ( a )
Out [ 1 ] : int

Встроенная функция typeQ сообщает тип переданного ей объекта. Это
может быть как встроенный тип, так и созданный в программе класс. Во втором случае возвращаемая информация зависит от описания, заложенного
программистом в классе. Как известно, в Python “ все что угодно является объектом”. А это значит, что даже такие простые типы данных, как int , обладают
собственными методами. Например, метод bit length ( ) позволяет узнать
количество битов, требуемых для хранения объекта int в памяти.

_

_

In [ 2 ] : a . bit length ( )
Out [ 2 ] : 4

Чем больше значение, хранящееся в объекте int, тем больше места он за нимает в памяти.
In [ 3] : а

= 100000

a . bit _length ( )

0ut [ 3] : 17

У различных классов и объектов столько всяких методов, что запомнить их
совершенно невозможно. Во многие интерпретаторы, в частности в IPython,
встроены интерактивные подсказки, позволяющие узнать все методы, под держиваемые объектом. Чтобы получить подсказку, достаточно ввести имя
объекта, точку и нажать клавишу . Также в Python имеется встроенная
функция d i r ( ) , возвращающая список всех атрибутов и методов переданного
ей объекта.
1

В Cython ( www . cython . org ) добавлена возможность статической типизации и компиляции,
характерных для С. Фактически Cython это полнофункциональный гибридный язык программирования, объединяющий возможности Python и С.



94

Глава 3

Особенностью Python является то , что целые числа могут иметь произвольную длину. Возьмем, к примеру, число гугол ( 10100) . Python без проблем
справляется с ним.
In [ 4 ] : googol = 1 0 ** 1 0 0
googol
Out [ 4 ] : 10000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000

. _

In [ 5 ] : googol bit length ( )
0 ut [ 5 ] : 333

Длинные целые числа

(
I

Python позволяет работать с очень длинными целыми числами, по скольку интерпретатор выделяет для их хранения в памяти ровно
столько битов / байтов , сколько требуется.

Арифметические операции с целыми числами записываются очень просто.
In [ 6 ] : 1 + 4
0 ut [ 6 ] : 5
In [ 7 ]: 1 / 4
Out [ 7 ] : 0.25
In [ 8 ] : t y p e ( l
0 ut [ 8 ] : float

/

4)

Числа с плавающей точкой
Результатом выражения 1 / 4 является число 0.252, имеющее тип float .
Когда к целому числу добавляется точка, например 1 . или 1.0, интерпретатор
Python начинает трактовать его как объект типа float . Результатом выраже ния , в котором один из операндов имеет тип float , тоже будет число с плава ющей точкой3.
In [ 9 ] : 1 . 6
0 ut [ 9 ] : 0.4

/

4

В Python 2 .x по умолчанию выполняется целочисленное деление, поэтому результат будет
другим. В Python 3.x целочисленное деление выполняется с помощью специального оператора ( например, результатом выражения 3 / / 4 будет 0).
3
Термины “ значение типа float” и “ объект типа float ” означают одно и то же, поскольку в
Python все числа объекты. То же самое справедливо и для любых других типов данных.
2



Типы данных и структуры Python

95

In [ 10 ] : type (1.6
Out [10 ] : float

/

4)

У типа float есть свои особенности, связанные с тем, что компьютерное
представление рациональных и вещественных чисел неточное и зависит
от реализации. В качестве иллюстрации определим объект b типа float .
При работе с такими объектами следует учитывать, что их внутреннее
представление всегда имеет определенную точность . Это становится очевидно
при добавлении 0.1, как показано ниже .
In [ И ] : b

= 035
type ( b )

Out [11] : float

In [12 ] : b + O. l
0ut [ 12] : 0.44999999999999996

Причина получения столь “ странного” результата заключается в том, что
объекты типа float внутренне хранятся в двоичном формате. В частности,
разряды вещественного числа представляются через элементы числового
у Z
X
1
к . . Для некоторых вещественных чисел такой ряд может
ряда п - л
2 4 8
включать очень большое или даже бесконечное количество элементов . Но по скольку разрядность числа ( количество битов , отведенных для его хранения)
так или иначе ограничена , возникает неизбежная погрешность округления.
Остальные числа могут быть представлены точно, поэтому даже при конеч ной разрядности они всегда воспроизводятся правильно . Рассмотрим следующий пример.



In [13] : с

= 0.5
с . as _ integer _ ratio ( )
Out [ 13] : ( 1 , 2 )
Число 0.5 хранится в исходном виде, поскольку его числовой ряд имеет
конечную длину: 0.5 = 1 / 2 . А вот представление объекта b = 0 . 3 5 совсем
не такое, как можно было бы предположить исходя из простейшей записи ра ционального числа ( 0 . 3 5 = 7 / 20 ) .
In [ 14 ] : b . as_ integer _ ratio ( )
Out [ 14 ] : ( 3152519739159347 , 9007199254740992 )

Точность представления числа зависит от количества битов, выделяемых для его хранения. На всех платформах, на которых выполняется Python,

96

Глава 3

поддерживается стандарт IEEE 754, описывающий формат двойной точности ( https : / / ги . Wikipedia . огд /1^гкг / Число_ двойной_ точности ) , в котором
внутреннее представление числа имеет разрядность 64 бит, что соответствует
точности до 15-3> знака после запятой.
Все это имеет существенное значение в контексте финансовых вычислений, где зачастую требуется обеспечить точное или , по крайней мере, наи лучшее из возможных представление чисел. Например, при суммировании
большого массива чисел даже мизерная погрешность округления может в
конечном итоге привести к существенному отклонению от правильного результата.
Модуль decimal предоставляет в распоряжение программиста объект,
предназначенный для хранения вещественных чисел произвольной точности,
и ряд параметров, позволяющих избежать проблем с точностью представления таких чисел.
In [15]: import decimal
from decimal import Decimal
In [ 16 ] : decimal . getcontextQ
0ut [ 16 ] : Context ( prec = 28 , rounding = ROUND_ HALF EVEN , Emin = - 999999 ,
Emax = 999999 , capitals = l , clamp= 0 , flags =[ ] ,
traps = [ InvalidOperation , DivisionByZero , Overflow ] )

_

In [17]: d

= Decimal ( l ) / Decimal ( ll )
d
0ut [ 17 ] : Decimal ( ' 0.09090909090909090909090909091 ' )
Чтобы задать требуемую точность округления, необходимо поменять соответствующий атрибут объекта Context .
In [ 18 ] : decimal . getcontext ( ) . ргес

=

4

О

In [19]: е

= Decimal ( l ) / Decimal ( ll )
e
0ut [ 19 ] : Decimal ( ' 0.09091 ' )
In [20]: decimal . getcontextQ . prec

In [21]: f = Decimal ( l )

=

50

©

/ Decimal ( ll )

f
0ut [ 21 ] : Decimal ( ' 0.090909090909090909090909090909090909090909090909091 ' )

Типы данных и структуры Python

97

О Точность меньше, чем заданная по умолчанию.

© Точность больше, чем заданная по умолчанию.
В случае необходимости точность округления можно настроить в соответствии со спецификой задачи. Также можно работать с числами, имеющими
разную точность.
In [22]: g = d + е + f
9
Out[22]: DecimalC 0.27272818181818181818181818181909090909090909090909 ' )

(

и

Вещественные числа произвольной точности
Модуль decimal содержит объект, предназначенный для представления вещественных чисел произвольной точности. В финансовых вычислениях иногда требуется гарантировать повышенную
точность, превышающую возможности 64- разрядного стандарта
IEEE 754.

Булевы значения
В программировании результатом операции сравнения, например 4 > 3,
4.5 < = 3.25 или ( 4 > 3 ) and ( 3 > 2 ) , будет значение True или False.
В Python это зарезервированные ключевые слова. Полный список таких слов
хранится в модуле keyword .
In [23]: import keyword
In [24]: keyword . kwlist
Out[24]: [ ' False ' ,
' None ' ,
' True ' ,
' and ' ,
' as ' ,
' assert ' ,
' async ' ,
' await ' ,
' break ' ,
• class ' ,
' continue ' ,
' def ' ,
' del ' ,
' elif ' ,
' else ' ,

98

Глава 3

' except ' ,
' finally ' ,
' for ' ,
' from ' ,
' global ' ,
' if ' ,
' import ' ,
' in ' ,
' is ' ,
' lambda ' ,
' nonlocal ' ,
' not ' ,
' or ' ,
' pass ' ,
' raise ' ,
' return ' ,
' try ' ,
' while ' ,
' with ' ,
' yield ' ]

Значения True и False относятся к типу данных bool и называются булевы ми . В следующем коде выполняются различные операции сравнения над одни ми и теми же операндами.

О

I n [ 25] : 4 > 3
0u t [ 25] : True

I n [ 26 ] : t y p e ( 4 > 3 )
0 u t [ 26 ] : bool
I n [ 27 ] : t y p e ( F a l s e )
0 u t [ 27 ] : bool

I n [ 28 ] : 4 > = 3
0 ut [ 28 ] : True

©
©

I n [ 29 ] : 4 < 3
0 u t [ 29 ] : False

In [ 30 ] : 4 < = 3
Out [ 30 ] : False

©

In [ 3 1] : 4

©

==

3

Типы данных и структуры Python

99

Out [ 31] : False
I n [ 32 ] : 4 ! = 3
Out [ 32 ] : True

©

О Больше.

© Больше или равно.
© Меньше.

© Меньше или равно.
© Равно.
© Не равно.
Очень часто над булевыми значениями выполняются логические операции,
результатом которых тоже будет объект bool.
I n [ 33 ] : True and True
Out [ 33 ] : True
I n [ 34 ] : True and False
Out [ 34 ] : False

I n [ 35 ] : False and False
Out [ 35 ] : False
I n [ 36 ] : True or True
0u t [ 36 ] : True

I n [ 37 ] : True or False
Out [ 37 ] : True
I n [ 38 ] : False or False
Out [ 38 ] : False
I n [ 39 ] : not True
0ut [ 39 ] : False

I n [ 40 ] : not False
Out [ 40 ] : True

Логические операторы и операторы сравнения разрешается свободно ком бинировать.
100

Глава 3

In [ 41] : ( 4 > 3 ) and ( 2 > 3 )
0 ut [ 41] : False
In [ 42 ] : ( 4 == 3 ) or ( 2 ! = 3 )
0 ut [ 42 ] : True
In [ 43 ] : not ( 4 i = 4 )
0 ut [ 43 ] : True

In [ 44 ] : ( not ( 4 ! = 4 ) ) and ( 2
0 ut [ 44 ] : False

==

3)

Чаще всего логические выражения применяются в условных конструкци ях, определяющих порядок выполнения программы.
In [ 45 ] : if 4 > 3 : О
print ( ' Условие истинно ' )
Условие истинно

=0 ©
while i < 4 : О
print ( ' Условие истинно , i
i += 1 ©
Условие истинно , i = 0
Условие истинно , i = 1
Условие истинно , i = 2
Условие истинно , i = 3

©

In [ 46 ] : i

= ',

i)

©

О Если условие истинно, то выполняется следующий код.

© Код, который выполняется в случае истинности условия.
© Инициализация переменной i значением 0.

О Следующий код выполняется ( многократно) до тех пор, пока условие
истинно.

© Вывод текста и значения переменной i.

© Увеличение переменной i на единицу; выражение i + =
выражению i

=

1 равнозначно

i + l.

В числовых выражениях булево значение True заменяется на 1, а значение
False
на 0. Но когда числа приводятся к типу bool с помощью функции
bool ( ) , число 0 преобразуется в False, а все остальные числа в Тгие.





Типы данных и структуры Python

101

In [ 47 ] : int(True)
Out [ 47 ] : 1
In [48]: int(False)
Out [ 48 ] : О
In [ 49 ] : float(True)
Out [ 49 ] : 1.0

In [ 50 ] : float(False)
Out [ 50 ] : 0.0
In [ 51 ] : bool ( 8 )
Out[ 51 ] : False

In [ 52 ] : bool(0.0)
Out[ 52 ] : False
In [ 53 ] : bool ( l )
0ut[ 53 ] : True

In [ 54 ] : bool(10.5)
Out [ 54 ] : True
In [ 55 ] : bool(- 2 )
Out[ 55 ] : True

Строки
После знакомства с числовыми типами настало время поговорить о строковых значениях. В Python им соответствует тип str . Объект str обладает целым
рядом встроенных методов, что является одним из преимуществ Python в пла не обработки строковых данных и текстовых файлов. Строковое значение берется в одинарные или двойные кавычки. Также можно преобразовать другой
объект в строку с помощью функции s t r ( ) (она использует либо стандартное
представление объекта, либо представление, заданное пользователем).
In [ 56 ] : t

=

' this

is a string object '

С помощью встроенных методов можно, например, сделать первую букву
строки прописной.
In [ 57 ] : t . capitalizeQ
Out[ 57 ] : ' This i s a string object '

102

Глава 3

Также можно разбить строку на слова, получив список слов ( объекты list
будут рассматриваться далее).

.

I n [ 58 ] : t splitO
0ut [ 58 ] : [ ' this ' , ' i s ' , ' a ' ,

' string ' , ' object ' ]

Можно выполнить поиск слова в строке и определить позицию (индекс)
первой буквы слова в строке.

.

I n [ 59 ] : t find ( ' string ' )
Out [ 59 ] : 10

Если слово не найдено в строке, метод find ( ) вернет значение - 1.

.

I n [ 60 ] : t find ( ' Python ' )
Out [ 60 ] : - 1

Для замены символов в строке применяется метод replace ( ).

.

I n [ 61] : t replace ( ' ' , ' | ' )
0ut [ 61] : ' this | i s | a | string | object '

Другой полезный метод — strip( ), позволяющий удалять начальные или
конечные символы строки.
I n [ 62 ] :
Out [ 62 ] :

' http : / / www . python . org ' . strip ( ' http: / / ' )
' www python org '

.

.

Наиболее часто применяемые строковые методы Python перечислены в
табл. 3.1.

.

Таблица 3.1 Полезные методы для работы со строками
Метод

Аргументы

Результат

capitalizeQ



Создает копию строки, в которой первая буква прописная

countQ
encodeQ

sub [

[ encoding[ , errors ] ]

Возвращает закодированную версию строки

find ( )

sub [

Возвращает позицию первого вхождения подстроки
sub

join ( )

seq

Выполняет конкатенацию строки в последовательности
seq

replace ( )

old, new [ , count ]

Заменяет подстроку о Id подстрокой new указанное
число раз ( count )

splitQ

[ sep[ , naxsplit ] ]

Возвращает список слов строки, используя символ sep
в качестве разделителя

f

f

s t a r t [ , end ] ]

start [

s

end ] ]

Подсчитывает число вхождений подстроки sub

Типы данных и структуры Python

103

Окончание табл. 3.1
Метод

Аргументы

Результат

splitlinesQ

[ keepends ]

Разбивает строку на подстроки в тех местах, где встречаются символы конца строки, оставляя сами символы,
если параметр keepends равен True

stripQ

chars

Создает копию строки, из которой удалены все начальные и конечные символы chars

upperQ



Возвращает копию строки, в которой все символы переведены в верхний регистр

Строки в кодировке Unicode
Ключевое отличие версии Python 3.7 от Python 2.7 заключается в
возможности кодирования и декодирования строк, а также в под держке стандарта Unicode (http: / / bit Iy / lx 41ytu). Мы не будем
углубляться в эту тему, поскольку в книге нам предстоит работать
преимущественно с числовыми данными, а используемые текстовые строки будут содержать стандартные символы.

.

Пример: вывод и замена строк
Для вывода на экран строк или строковых представлений объектов в

Python применяется функция printQ.
In [ 63 ] : print ( ' Python for Finance ' )

Python for Finance
In [ 64 ] : print ( t )

©

this is a string object
In [ 65 ] : i = 0

while i < 4 :
print (i)
i += 1

©

0
1
2
3
In [ 66 ] : i = 0

while i < 4 :
print (i, e n d= ' | ' )

104

Глава 3

©

О

i += 1

01112131

О Выводит на экран заданную строку.

0 Выводит на экран значение строковой переменной.

0 Выводит на экран строковое представление целого числа (объекта int).
0 Добавляет в конец выводимой строки завершающий символ (по умолчанию используется символ конца строки \ п).

В Python поддерживаются удобные операции замены строк. Старый способ
предполагает использование оператора %. Новый способ основан на исполь зовании фигурных скобок ({}) и метода format ( ). На практике применяются
оба способа. Мы не будем углубляться в детали, рассмотрев только самые важ ные варианты. Сначала приведем примеры замены строк старым способом.

О

In [ 67 ] :
Out [ 67 ] :

' Это целое %d '
' Это целое 15 '

In [ 68 ] :
0ut [ 68 ] :

' Это
' Это

In [ 69 ] :
0u t [ 69 ] :

' Это целое %04d '
' Это целое 0015 '

In [ 70 ] :
Out [ 70 ] :

' Это вещественное %f % 15.3456 О
' Это вещественное 15.345600 '

In [ 71] :
Out [ 71] :

' Это вещественное % . 2 f ' %
' Это вещественное 15.35 '

In [ 72 ] :
0ut [ 72 ] :

' Это вещественное %8 f ' % IS . 3456
' Это вещественное 15.345600 '

In [ 73 ] :
Out [ 73 ] :

' Это вещественное
' Это вещественное

In [ 74 ] :
0ut [ 74 ] :

' Это вещественное %08.2 f '

In [ 75 ] :
0u t [ 7 S ] :

' Это
' Это

% 15

целое %4d ' % 15
целое
15 '
% 15

©
©

15 . 3456

©

©

%8.2 f ' % 15.3456

©

15.35 '

% 15.3456
Это
вещественное
00015.35
'
'
строка % s ' % 1 Python '
строка Python '

©

©

Типы данных и структуры Python

105

In [ 76 ] : ' Это строка % 10s ' % ' Python '
Python '
0ut [ 76 ] : ' Это строка

©

О Замена объектом int .
© Замена объектом int с фиксированным количеством символов.
© Замена объектом int с дополнением ведущими нулями в случае необходимости.

О Замена объектом float .

© Замена объектом float с фиксированным количеством знаков после
запятой.

© Замена объектом float с фиксированным количеством символов (при
необходимости добавляются нулевые разряды ).

© Замена объектом float с фиксированным количеством символов и фиксированной точностью.

© Замена объектом float с фиксированным количеством символов и фик-

сированной точностью ( при необходимости добавляются ведущие нули).

© Замена строкой.
© Замена строкой с фиксированным количеством символов.
Теперь выполним те же самые операции новым способом. Обратите внимание отличие в последнем примере.
In [ 77 ] : ' Это целое { : d } ' . format ( 15 )
Out [ 77 ] : ' Это целое 15 '
In [ 78 ] : ' Это целое { : 4d } ' . format ( 15 )
Out [ 78 ] : ' Это целое
15 '
In [ 79 ] : ' Это целое { : 04d ) ' . format ( 15 )
Out [ 79 ] : ' Это целое 0015 '
In [ 80 ] : ' Это вещественное { : f } ' . format ( 15.3456 )
Out [ 80 ] : ' Это вещественное 15.345600 '
In [ 81 ] : ' Это вещественное { : . 2 f } ' . format ( 15.3456 )
Out [ 81 ] : ' Это вещественное 15.35 '
In [ 82 ] : ' Это вещественное { : 8 f } ' , format ( 15.3456 )

106

Глава 3

0 ut [ 82 ] : ' Это вещественное 15.345600 '

.

In [ 83 ] : ' Это вещественное {:8.2f } ' format ( 15.3456 )
Out [83] : ' Это вещественное
15.35 '

.

In [ 84 ] : ' Это вещественное {: 08.2f } ' format ( 15.3456 )
0 ut [ 84 ] : ' Это вещественное 00015.35 '

.

In [ 85 ] : ' Это строка {: s} ' format ( ' Python ' )
0 ut [ 85 ] : ' Это строка Python '

.

In [ 86 ] : ' Это строка {: 10s} ' format ( ' Python ' )
'
0 ut [ 86 ] : ' Это строка Python

Замена строк часто применяется при многократном выводе меняющихся
данных , например в цикле while .
In [ 87 ] : i = 8
while i < 4 :
prlntC Число равно %d ' % l )
i += 1
Число равно 0
Число равно 1
Число равно 2
Число равно 3
In [ 88 ] : 1 = 0
while i < 4 :
print ( ' the number i s { : d} ' format ( i ) )
i += l
Число равно 0
Число равно 1
Число равно 2
Число равно 3

.

Пример: регулярные выражения
Для работы со строковыми объектами часто применяются регулярные вы ражения. Соответствующие функции содержатся в модуле ге .
In [ 89 ] : import re

Представьте себя на месте финансового аналитика, который получил боль шой текстовый файл , например CSV- файл, содержащий временные ряды.
Чаще всего информация о дате/ времени представляется в формате, с которым
Типы данных и структуры Python

107

код Python не может работать напрямую. Для преобразования такой информации в правильный формат нужны регулярные выражения. В качестве при мера рассмотрим строковый объект, содержащий три элемента даты / времени,
три целочисленных значения и три строки. Обратите внимание на то, что зна чение объекта заключено в тройные кавычки. Это позволяет разбивать запись
на несколько строк.
In [90]: series = " " "
' 01 / 18 / 2014 13 : 00 : 00 ' , 100 , ' 1st ' ;
' 01 / 18 / 2014 13 : 30 : 00 ' , 110 , ' 2 nd ' ;
' 01 / 18 / 2014 14 : 00 : 00 ' , 120 , ' 3 rd '

Следующее регулярное выражение описывает применяемый формат даты/
времени4.
In [91]: dt

=

re . compile ( " ' [ 0 - 9 / : \ s ] + ' " ) # дата и время

Воспользовавшись им, вы легко найдете все значения даты/ времени. В та кого рода задачах поиск с помощью регулярных выражений выполняется бы стрее, чем с помощью стандартных средств.

= dt . findall ( series )
result
Out[92]: [ '" 01 / 18 / 2014 13 : 00 : 00 ’ " , '" 01 / 18 / 2014 13 : 30 : 00 ' " ,
'" 01 / 18 / 2014 14 : 00 : 00 ' " ]
In [92]: result

Регулярные выражения

(

Сложные операции поиска подстрок в строковых объектах лучше
выполнять с помощью регулярных выражений, которые обеспечи\ вают преимущества с точки зрения производительности.

Все найденные строковые значения даты / времени можно преобразовать
в объекты специального типа
datetime ( подробнее эти операции рассмотрены в приложении А ). Для этого необходимо указать структуру хранимого значения.



In [93]: from datetime import datetime
pydt = datetime . strptime ( result [ 0 ] . replace ( " ' " , " " ) ,
' °/om / %d / %Y %H : %M : %S ' )

pydt
4

Здесь мы не будем углубляться в детали таких выражений. В Интернете можно найти множество информации о регулярных выражениях в целом и их синтаксисе в Python в частности.
Хорошее введение в тему регулярных выражений содержится в книге Фицджеральда [ 4].

108

Глава 3

Out [ 93 ] : datetirne . datetime ( 2014 , 1, 18 , 13 , 0 )

In [ 94 ] : print ( pydt )
2014 - 01- 18 13 :00 : 00
In [ 95 ] : print ( type ( pydt ) )
< class

' datetime . datetime ' >

Детальнее о работе со значениями даты и времени, а также о применяемых
для этого объектах date tine и их методах мы поговорим в следующих главах.
А пока что лишь обозначим важность этой темы в контексте финансовых рас четов.

Основные структуры данных
Структуры данных — это объекты, которые могут содержать произволь ное число других объектов. В Python поддерживаются следующие встроенные
структуры данных.
tuple
Неизменяемый набор произвольных объектов с поддержкой всего нескольких методов.

list
Изменяемый набор произвольных объектов с поддержкой большого количества методов.

diet

Набор объектов, доступ к которым осуществляется по ключу,
set

Неупорядоченный набор уникальных объектов.

Кортежи
Кортеж ( объект tuple) — достаточно простая структура ограниченного
применения. Элементы кортежа перечисляются через запятую и заключаются
в круглые скобки.
In [ 96 ] : t = ( 1, 2 . 5 ,
type ( t )
0ut [ 96 ] : tuple

' data' )

Типы данных и структуры Python

109

Допускается не указывать круглые скобки, перечисляя только сами объекты через запятую.

= 1 , 2.5 , ' data '
type ( t )

In [ 97 ] : t

Out[ 97 ] : tuple

Элементы кортежа, как и в случае остальных структур данных, имеют
встроенную индексацию, что позволяет обращаться к ним по отдельности.
Важно помнить о том, что в Python индексация ведется с нуля, а не с единицы.
Таким образом, третий элемент кортежа будет иметь индекс 2.
In [ 9 (3 ] : t [ 2 ]
Out[ 98 ] : ' data '
In [ 99 ] : type ( t [ 2 ] )
0ut [ 99 ] : str

Индексация с нуля

(

В отличие от некоторых других языков программирования , например Matlab, в Python применяется индексация с нуля. Это означает,
i что первый элемент кортежа всегда имеет индекс 0.

У объекта tuple всего два метода: countQ и indexQ . Первый возвращает
количество экземпляров заданного объекта в кортеже, а второй возвращает
индекс первого из них.
In [100 ] : t . count ( ' data ' )
Out [ 100 ] : 1
In [101] : t . index ( l )
Out [ 101] : 0



Кортежи неизменяемые объекты. Это означает, что созданный объект не
подлежит модификации.

Списки



Список ( объект list )
более гибкая и функциональная структура данных, чем кортеж. Списки широко применяются в финансовых вычислениях,
например для хранения регулярно обновляемых котировок акций. Элементы списка перечисляются через запятую и заключаются в квадратные скоб ки. Базовая функциональность списков напоминает функциональность кор тежей.

110

Глава 3

In [102]: l

= [1, 2.5,

' data ' ]

1[ 2 ]
Out [ 1 0 2 ] : ' data '
Списки можно также создавать с помощью функции list ( ) . В следующем
примере созданный ранее кортеж преобразуется в список.

= list(t)
l
Out[103]: [ 1 , 2 . 5 , ' data ' ]
I n [103]: I

In [104]: type(l)
Out [ 104 ] : l i s t

В отличие от кортежей и строк списки можно расширять и сокращать с помощью различных методов и операций, поскольку это изменяемые объекты.
В частности, можно присоединить список к существующему списку.
In [ 105 ] : l . append ( [ 4 , 3 ] )

О

I
Out [ 105 ] : [ 1 , 2 . 5 , ' data ' , [ 4 , 3 ]]
I n [106]: l . extend([1.0, 1 . 5 , 2 ,0])

©

l
Out [ 106 ] : [ 1 , 2 . 5 , ' data ' , [ 4 , 3 ] , 1 . 0 , 1 . 5 , 2 . 0 ]
In [107]: l . i n s e r t ( l , ' i n s e r t ' )

©

l
Out [ 1 0 7 ] : [ 1 , ' i n s e r t ' , 2 . 5 , ' data ' , [ 4 , 3 ] , 1 . 0 , 1 . 5 , 2 . 0 ]
I n [108]: l . remove ( ' data ' )

0

l
Out [ 1 0 8 ] : [ 1 , ' i n s e r t ' , 2 . 5 , [ 4 , 3 ] , 1 . 0 , 1 . 5 , 2 . 0 ]
I n [109]: p = l . pop ( 3 ) ©
p r i n t ( l , p)
[1, ' i n s e r t ' , 2 . 5 , 1 . 0 , 1 . 5 , 2 . 0] [4 , 3]

О Добавление списка в конец другого списка.
0 Добавление элементов в список.

© Вставка объекта в указанную позицию списка.
О Удаление первого экземпляра объекта из списка.
Типы данных и структуры Python

111

© Удаление объекта из списка по указанной позиции; метод возвращает
извлеченный объект.

При работе со списками можно создавать срезы, т.е. фрагменты исходного
списка.
I n [ 110 ] : I [ 2 : 5 ] О
O u t [ 110 ] : [ 2 . 5 , 1 . 0 , 1 . 5 ]

О Срез включает с третьего по пятый элементы исходного списка.
Наиболее часто используемые операции и методы списков перечислены в
табл. 3.2.

.

Таблицы 3.2 Методы объекта list и операции со списками
Метод

Щ]

=

х

=

Аргументы

Описание

Ш

Заменяет i - й элемент объектом х

Заменяет каждый к-\л элемент в диапазоне от i
до j - 1 объектом s
Добавляет объект х в конец списка
Посчитывает количество экземпляров объекта х
Удаляет каждый к-й элемент в диапазоне от г до

s

append

( x)

count

( x)

del l[ i : j: /r]

7

- 1

Включает в список все элементы объекта s

extend
index

( x[ , i [ ,

insert

( i , x)

Вставляет объект х перед позицией с индексом г

remove

( x)

Удаляет первое вхождение объекта х

pop

Удаляет элемент с индексом i и возвращает его
Изменяет порядок элементов на обратный

'(, [ cnp [ , key , r e v e r s e ] ] ] ) Сортирует
элементы списка указанным способом
\

( s)
j]] )

Определяет индекс первого экземпляра х, встречающегося в диапазоне от i до j - 1

( i)

reverse
sort

Пример: управляющие конструкции
Несмотря на то что это отдельная тема, знакомство с управляющими конструкциями Python, такими как цикл for , лучше всего начинать в контексте
списков. Дело в том, что, в отличие от других языков программирования, в
Python циклы применяются по отношению к объектам list. Рассмотрим пример, в котором цикл for применяется для извлечения элементов списка I с
112

Глава 3

индексами от 2 до 4, возведения их в квадрат и вывода полученных значений на
экран. Обратите внимание на отступ второй строки он необходим для струк турирования кода и означает, что данная строка выполняется в цикле for.



In [ 111 ] : for element i n l [ 2 : 5 ] :
print ( element ** 2 )
6.25
1.0
2.25

Это более гибкий подход по сравнению со стандартными циклами, основанными на использовании счетчиков. Последние тоже поддерживаются в
Python , но реализуются с помощью объекта range.
In [ 112 ] : г

г

=

range ( 0 , В , 1)

О

0ut [ 112 ] : range ( 0 , 8 )

In [ 113] : type ( r )
0ut [ 113 ] : range

О Параметрами конструктора являются начальный элемент диапазона, конечный элемент диапазона и шаг приращения.
Для сравнения создадим аналогичный цикл с использованием объекта
range.
In [ 114 ] : for i i n range ( 2 , 5 ) :
print ( l [ i ] ** 2 )
6.25
1.0
2.25

Просмотр элементов списка
В Python можно циклически перебирать элементы произвольных
списков, что избавляет от необходимости использовать счетчики.

(
I

В Python имеются и типичные условные конструкции if , elif и else, которые применяются так же, как и в других языках программирования.
In [ 115 ] : for i in range ( l , 10 ) :
if i % 2 == 0 : О
print ( " %d четное " % i )
elif i % 3 == 0 :
print ( " %d кратно 3 " % i )

Типы данных и структуры Python

113

else :
print ( " %d нечетное " % i )

1
2
3
4
5
6
7
8
9

О Здесь %

нечетное
четное
кратно 3

четное
нечетное

четное
нечетное
четное
кратно 3



это оператор деления по модулю.

Циклическую обработку элементов можно реализовать и в цикле while .
In [ 116 ] : total = 0
while total < 100 :

total + = 1
print ( total )
100

Особенностью Python являются так называемые списковые включения, которые позволяют компактно генерировать списки в циклах, вместо того чтобы циклически перебирать существующие списки.
In [ 117 ] : гп = [ i ** 2 for i in range ( S ) ]
m
Out [ 117 ] : [ 0 , 1 , 4 , 9 , 16 ]

В определенном смысле подобные списковые включения, в которых код
цикла задан фактически в неявном виде, можно считать первым шагом на
пути к векторизации вычислений ( о векторизации кода мы поговорим в главах 4 и 5) .

Пример: функциональное программирование
Python включает целый ряд средств функционального программирования,
например функции f i l t e r Q , map ( ) и reduceQ , которые можно применять
ко всему набору входных данных ( в нашем случае ко всему содержимому
объекта l i s t ) . Но сначала необходимо разобраться с тем, как объявляется
функция. Создадим простейшую функцию f ( ) , возвращающую квадрат передаваемого ей аргумента х.



114

Глава 3

In [ 118 ] : def f ( x ) :
return x
f ( 2)
Out [ 118 ] : 4

**

2

Реальные функции могут быть сколь угодно сложными, с множеством
аргументов и вариантов возвращаемых значений. Рассмотрим следующую

функцию.
In [ 119 ] : def even( x ) :
return x % 2 == 0
even( 3 )
0ut [ 119 ] : False

Она возвращает булево значение. Такую функцию можно применить сразу
ко всему списку с помощью функции пар ( ).
In [ 120 ] : list ( map( even, range( 10 ) ))
Out [ 120 ]: [ True, False, True, False, True, False, True, False,
True, False ]

Более того, в качестве аргумента функции пар ( ) можно передать определе функции с помощью ключевого слова lambda. Такие анонимные функции называются лямбда - функциями.

ние анонимной

In [ 121] : list ( map( lambda х : х ** 2, range( 10 )) )
Out [ 121] : [ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

С помощью функций можно фильтровать списки. В следующем примере
функция filter ( ) возвращает элементы списка, которые соответствуют 6улевому условию, заданному в функции even ( ) .
In [ 122 ] : list ( filter ( even, range ( 15 )) )
Out[ 122 ] : [ 0, 2, 4, 6, 8, 10, 12, 14 ]

Щ

Списковые включения, функциональное программирование и анонимные
функции
В Python считается хорошей практикой всячески избегать ис пользования циклов. В этом смысле списковые включения и средства функционального программирования, в частности функции
filter ( ), map ( ) и reduce ( ), помогают писать более компактный и
понятный код без (явных) циклов. Лямбда- функции служат тем же

.

самым целям

Типы данных и структуры Python

115

Словари



это изменяемые наборы объектов, индексируСловари ( объекты diet )
емые специальными ключами ( чаще всего строками ). В отличие от списков,
словари не упорядочены и не сортируются5. Элементы словаря перечисляются
через запятую и заключаются в фигурные скобки. Следующий пример иллюстрирует разницу между списками и словарями.

={

In [123] : d

' Имя ' : ' Ангела Меркель 1 ,
' Страна ' : ' Германия ' ,
' Должность ' : ' Канцлер ' ,
' Возраст ' : 64
}
type ( d )
Out [ 123 ] : diet
In [ 124 ] : print ( d [ ' Имя ' ] , d [ ' Возраст ' ] )
Ангела Меркель 64

У словарей имеется множество встроенных методов.

.

_

.

_

.

_

In [125 ] : d keysQ
Out [ 125 ] : dict keys ( [ ' Имя ' , ' Страна ' , ' Должность ' , ' Возраст ' ] )

In [126 ] : d valuesQ
0 ut [126 ] : dict values ( [ ' Ангела Меркель ' , ' Германия ' , ' Канцлер ' , 64 ] )
In [ 127 ] : d itemsQ
Out [127 ] : dict items ( [ ( ' Имя ' , ' Ангела Меркель ' ) , ( ' Страна ' ,
' Германия ' ) , ( ' Должность ' , ' Канцлер ' ) ,
( ' Возраст ' , 64 ) ] )
In [128] : birthday = True
if birthday :
d [ ' Возраст ' ] += l
print ( d [ ' Возраст ' ] )
65

На основе словаря можно получить объект iterator , который применяется в циклах подобно спискам.

5

Существует подкласс словарей OrderedDict , хранящий сведения о порядке его заполнения
элементами ( https : / / docs . python . org / 3 / library / collections html ).

.

116

Глава 3

.

In [ 129 ] : for item in d items ( ) :
print ( item)
( ' Имя ' , ' Ангела Меркель ' )
( ' Страна ' , ' Германия ' )
( ' Должность ' , ' Канцлер ' )
( ' Возраст ' , 65 )
In [ 130 ] : for value in d . valuesQ :
print ( type ( value ) )


1 a

.append (

1

string 1 )

©

ТуреЕггог : must be real number , not s t r

.

I n [ 18 ] : a tollstQ ©
0u t [ 18 ] : [ 0.5, 0.75, 1.0, 1.5, 2.0, 0.5 , 5.0, 6.75 ]

О В исходный массив разрешается добавлять только объекты float. Во
всех остальных случаях выдается сообщение об ошибке.

© В случае необходимости массив можно преобразовать обратно в список.

Работа с массивами NumPy

125

Преимуществом класса array является наличие встроенных методов для
работы с файлами.
In [ 19 ] : f = open ( ' array . ару ' , ' wb ' )
a . tofile ( f ) ©
f . close ( ) ©
In [ 28 ] : with open ( ' array . apy ' ,
a . tofile ( f ) О
In [ 21] : ! ls - n arr * ©
- rw - r - - r - - @ 1 503

О

' wb ' ) as f :

32 Nov

20

0

7 11: 46 array . apy

О Открытие файла на диске для записи двоичных данных.

© Запись содержимого объекта a r r a y в файл.
© Закрытие файла.
© Альтернативный синтаксис с использованием конструкции with.
© Просмотр информации о файле.
При считывании данных с диска необходимо учитывать их тип.
In [ 22 ] : b = array . аггау ( ' f ' )

О

In [ 23 ] : with open( ' array . apy ' , ' rb ' ) as f :
b . fromfile ( f , 5 ) ©

©

In [ 24 ] : b ©
Out [ 24 ] : arrayOf ' , [ 0.5 , 0.75 , 1.0 , 1.5 , 2.0 ] )
In [ 25 ] : b = array . array ( ' d ' )

О

In [ 26 ] : with open ( ' array . apy ' , ' rb ' ) as f :

b . fromfile ( f , 2)

©

In [ 27 ] : b ©
Out [ 27 ] : array ( ' d ' , [ 0.0004882813645963324 , 0.12500002956949174 ] )

О Объявление нового массива с типом данных float.

© Открытие файла для чтения двоичных данных.

© Считывание пяти элементов в массив Ь.
126

Глава 4

О Объявление нового массива с типом данных double.

©

Считывание двух элементов из файла.

© Изменение типа данных массива приводит к считыванию неправильного
формата чисел.

Обычные массивы NumPy
Создание массивов на основе списков — не самый рациональный подход.
В конце концов, класс list создавался не с этой целью У списков более широ кая область применения, чем у объектов array. Полезнее было бы иметь более
специализированный класс для работы с различного вида массивами.

.

Основные операции
И такой класс есть: это numpy . ndarray. Он специально создавался для
удобной и эффективной работы с многомерными массивами. Рассмотрим

конкретные примеры.
I n [ 2 8 ] : i m p o r t numpy as np

О

©

I n [ 2 9 ] : a = n p . a r r a y ( [ 0 , Q . S , 1.0, 1.5, 2 . 0 ] )

a
0u t [ 2 9 ] : a r r a y ( [ 0 . , 0 . 5 , 1. , 1 . 5 , 2 . ] )
I n [ 30 ] : type ( a ) ©
O u t [ 3 0 ] : numpy . n d a r r a y

I n [ 31 ] : a = np . a r r a y ( [ ' a ' ,

a
0u t [ 3 1 ] : a r r a y ( [ ' a ' , ' b ' ,

1

' b' , ' c ' ])

©

c ' ] , d t y p e = ' 1 г + s

ValueError : operands could not be broadcast together
with shapes ( 4 , 3 ) ( 4 , )
In [142 ] : r . transposeQ + s

©
Работа с массивами NumPy

147

Out [ 142 ] : аггау([[ 0, 6, 12, 18],
[ 1, 7, 13, 19],
[ 2, 8, 14, 20]])

.

= s reshape ( - l , 1)
sr
0 ut [ 143 ] : array ( [[ 0 ] ,
[3],
[ 6],
[9]])
In [143 ] : sr

In [ 144 ] : sr . shape
Out [ 144 ] : (4, 1)

©

©

In [145 ] : г + s . reshape( - 1, 1)
Out [ 145 ] : аггау([[ 0, 1, 2],

©

[ 6, 7, 8],
[12, 13, 14],
[18, 19, 20]])

О Новый одномерный массив ndarray длиной 3.
© Объекты г (матрица ) и s ( вектор ) суммируются напрямую.
© Еще один одномерный массив ndarray длиной 4.

0

Длина нового вектора s теперь отличается от второй размерности

объекта г .

© Транспонирование объекта г снова делает возможной векторизованную
операцию сложения.



поменять форму объекта s на ( 4 , 1)
( результирующие матрицы получаются разными ).

© Альтернативный вариант

К объектам ndarray можно применять пользовательские функции Python.
В подобного рода операциях массивы могут использоваться так же, как значения типа int или float . Рассмотрим следующую функцию.
In [146 ] : def f(х):
return 3 * х + 5
In [147 ] : f ( 0.5 ) ©
Out [147 ] : 6.5
In [148 ] : f ( г )

148

Глава 4

©

©

Out[148 ] : аггау ( [[ 5 ,

8 , 11 ] ,
[ 14 , 17 , 20 ] ,
[ 23 , 26 , 29 ] ,
[ 32 , 35 , 38 ]] )

О Простая функция, выполняющая линейное преобразование параметра х .

0 Функция f ( ) применяется к значению типа float .

0 Та же самая функция применяется к массиву ndarray векторизованно и
поэлементно.

Как видите, функция f ( ) применяется к объекту NumPy поэлементно.
В этом смысле программист не избегает циклов. Они просто не создаются на
уровне кода Python, но применяются на уровне библиотеки NumPy, где соответствующий код оптимизирован на языке С, а потому работает быстрее, чем
чистый код Python. Именно этим объясняется высокая производительность
операций с массивами в NumPy.

Эффективное использование памяти
Рассмотренный ранее метод np . zeros ( ) , с помощью которого инициали зируется объект ndarray , поддерживает необязательный аргумент, задающий
способ хранения массива в памяти. Грубо говоря, этот аргументопределяет,
какие элементы оказываются смежными в памяти. В случае небольших массивов разницы с точки зрения производительности не будет никакой. Но в
определенных финансовых приложениях, работающих с большими массива ми, разница начинает проявляться ( см., например, статью Memory layout of
multi-dimensional arrays, доступную по адресу https : / / bit . ly / 2 K 8 rujN ) .
Чтобы понять, насколько сильно способ хранения массива в памяти влия ет на производительность финансовых вычислений, рассмотрим следующий
код.
In [ 149 ] : х

= np . random . standard _normal ( ( 1000000 ,

In [150 ] : у

=

2 * х + 3

5) )

О

©

In [ 151] : С = пр . аггау ( ( х , у ) , order = ' C ' )

©
О

In [152 ] : F

=

пр . аггау ( ( х , у ) , order = ' F ' )

In [ 153 ] : х

=

0.0 ; у

0.0

©
Работа с массивами NumPy

149

In [154]: C[:2].round(2) ©
0ut[154]: array([[[ - 1.75, 0.34, 1.15, - 0.25, 0.98],
[ 0.51, 0.22, - 1.07, - 0.19, 0.26],
[ - 0.46, 0.44, - 0.58, 0.82, 0.67],

[ - 0.05, 0.14, 0.17, 0.33, 1.39],
[ 1.02, 0.3 , - 1.23, - 0.68, - 0.87],
[ 0.83, - 0.73, 1.03, 0.34, - 0.46]],
[[- 0.5 , 3.69, 5.31, 2.5 , 4.96],
[ 4.03, 3.44, 0.86, 2.62, 3.51],
[ 2.08, 3.87, 1.83, 4.63, 4.35],
[ 2.9 , 3.28, 3.33, 3.67, 5.78],
[ 5.04, 3.6 , 0.54, 1.65, 1.26],
[ 4.67, 1.54, 5.06, 3.69, 2.07]]])

О

Двухмерный объект ndarray с резкой асимметрией размерностей .

© Линейное преобразование исходного массива.

© Создание двухмерного объекта ndarray с порядком заполнения элемен тов ' С (построчно ) .

© Создание двухмерного объекта ndarray с порядком заполнения элемен тов ' F ' ( по столбцам) .

© Очистка памяти .
© Фрагмент массива С.
Теперь попробуем поработать с обоими массивами и оценим скорость выполнения типичных операций .
In [155]: %tineit C.sunQ О
4.36 ns ± 89.3 |JS per loop ( mean
100 loops each)

±

std. dev. of 7 runs,

In [156]: %timeit F.sunQ ©
4.21 ns ± 71.4 ps per loop ( nean
100 loops each )

±

std. dev. of 7 runs,

In [157]: %tineit C.sun(axis= G) ©
17.9 ns ± 776 ps per loop (nean
100 loops each)

150

Глава 4

±

std. dev. of 7 runs,

In [158 ] : % tineit C . sum ( axis = t ) ©
35.1 ns ± 999 ps per loop ( nean ± std . dev . of 7 runs ,
10 loops each )
In [ 159 ] : % tineit F . sun ( axis = 0 ) ©
83.8 ns ± 2.63 ns per loop ( nean ± std . dev . of 7 runs ,
10 loops each )

In [ 160 ] : %tineit F . sun ( axis = l ) ©
67.9 ns ± 5.16 ns per loop ( nean ± std . dev . of 7 runs ,
10 loops each )
In [161] : F

=

0.0 ; C

=

0.0

О Вычисление суммы всех элементов массива.
© Вычисление суммы элементов по строкам ( большое количество ).
Вычисление суммы элементов по столбцам ( малое количество).

@

Полученные результаты позволяют сделать следующие выводы.
• При суммировании всех элементов массива способ его хранения в памяти не играет особой роли.



Для массивов с порядком заполнения ' С ' суммирование выполняется
быстрее как по строкам, так и по столбцам (абсолютное преимущество
в скорости).

• Для массивов с порядком заполнения ' С ' (построчное хранение) сум мирование выполняется быстрее по строкам, чем по столбцам.



Для массивов с порядком заполнения ' F ' ( хранение по столбцам ) сум мирование выполняется быстрее по столбцам, чем по строкам.

Резюме



NumPy это основной пакет для работы с массивами в Python. Предоставляемый им класс ndarray специально предназначен для эффективных ма тричных вычислений. Наличие удобных методов и универсальных функций,
поддерживающих векторизованные операции, позволяет избегать медленных
циклов на уровне кода Python. Все это пригодится нам при работе с пакетом
pandas и его классом DataFrame в следующей главе.

Работа с массивами NumPy

151

Дополнительные ресурсы
Множество полезной информации о возможностях пакета NumPy можно
найти на официальном сайте ( www . numpy . org ).
Хорошим подспорьем в изучении NumPy будут следующие книги.

• McKinney, Wes. Python for Data Analysis ( 2017, O’ Reilly).
• VanderPlas, Jake. Python Data Science Handbook (2016, O’Reilly).

152

Глава 4

ГЛАВА 5

Анализ данных с помощью библиотеки
pandas

Факты! Факты! Факты! Я не могу лепить кирпичи без глины!

Шерлок Холмс

Эта глава посвящена библиотеке pandas, предлагающей инструменты ана лиза табличных данных. Библиотека не только содержит полезные классы и
функции, но и служит оболочкой к другим пакетам, что делает ее незаменимой для анализа финансовых данных.
В этой главе рассматриваются следующие фундаментальные структуры
данных.
Применение

Тип объекта

Описание

DataFrame

Двухмерный индексированный объект данных Табличные данные с разбивкой по столбцам

Series

Одномерный индексированный объект данных Временные ряды

Общая структура главы такова.
Класс DataFrame
В этом разделе описываются основные возможности класса DataFrame
на примере небольших наборов данных. Также показано, как преобразо вать объект ndarray библиотеки NumPy в объект DataFrame.
Основные аналитические возможности и инструменты визуализации
Мы познакомимся только с основными способами анализа и визуализации данных (более подробно эти темы рассматриваются в следующих
главах).

Класс Series
Данный раздел содержит краткое описание класса Series, который
представляет собой частный случай класса DataFrame, т.к. предназначен
для работы с одномерными рядами.

Группирование данных
Одна из ключевых особенностей класса DataFrame — возможность
группировки данных по одному или нескольким столбцам.
Сложные операции извлечения данных
В этом разделе показано, как применять сложные условия для извлечения данных из объекта DataFrame .

Конкатенация, соединение и слияние данных
В процессе анализа часто приходится комбинировать различные набо ры данных. Библиотека pandas предлагает множество способов решения этой задачи.

Производительность вычислений
Следуя идеологии Python, библиотека pandas позволяет решать одни и
те же задачи несколькими способами. В этом разделе будет показано,
как выбрать наиболее эффективный вариант.

Класс DataFrame
Основа библиотеки pandas — класс DataFrame, предназначенный для эф фективной обработки табличных данных (т.е. данных, разбитых по столбцам).

В частности, в нем имеются средства именования столбцов, а также поддер живается гибкая индексация записей, как в таблицах реляционных баз дан ных или электронных таблицах Excel.
В этом разделе вы познакомитесь с основными возможностями класса
DataFrame . Функциональность класса настолько широка, что охватить ее в
одной главе не получится. Мы будем неоднократно возвращаться к нему в по следующих главах.

Знакомство с классом DataFrame
Класс DataFrame изначально предназначен для работы с индексированными и размеченными данными, напоминающими таблицы реляционных баз
данных или рабочие листы Excel. Для начала рассмотрим, как создается объект DataFrame.
In [ 1] : import pandas as pd

О

In [ 2 ] : df = pd . DataFrame ( [ 10 , 20 , 30 , 40 ] ,
columns= [ ' Целые ' ] ,

index = [ ' a ' , ' b ' ,
154

Глава 5

©
©

' c ' , ' d ' ]) О

©

I n [ 3 ] : df
Out [ 3 ] :

Целые

a

10
20
30
40

b
c
d

О Импорт пакета pandas.
© Данные представляют собой список.
© Название столбца.

0 Индексы (метки).

в

Вывод содержимого объекта DataFrame, включая название столбца и
метки.

В этом простом примере продемонстрированы основные возможности
хранения данных в объектах DataFrame.



Сами данные могут быть какого угодно типа (list, tuple, ndarray, diet
и др.) -



Данные группируются в столбцы, которым присваиваются имена
(подписи).



Данные снабжаются индексами, которые могут иметь разный формат
( числа, строки, дата/время и т.п.).

Работать с объектом DataFrame удобнее и эффективнее, чем с обычным
объектом ndarray, который накладывает немало ограничений, когда нужно,
к примеру, увеличить существующий массив. С точки зрения производительности вычислений возможности этих двух классов примерно сопоставимы.
Ниже приведено несколько примеров выполнения типичных операций с объектом DataFrame .

.

I n [ 4 ] : df index О
0u t [ 4 ] : Index ( [ ' a ' , ' b ' ,

' c ’ , ' d ' ] , dtype= ' object ' )

.

I n [ 5 ] : df columns ©
Out [ 5 ] : Index ( [ ' Целые ' ] , dtype= ' object ' )
I n [ 6 ] : df

.locfc ' ]

©
Анализ данных с помощью библиотеки pandas

155

Out [ 6 ] : Целые
30
Name : c , dtype : int 64

In [ 7 ] : df . loc[[ ' a ' ,
0ut [ 7 ] :

1

d ' ]]

О

Целые
a
d

10
40

In [ 8 ] : df . Uoc[ l : 3 ]
0ut [ 8 ] :

©

Целые
b
c

20
30

In [ 9 ] : df . sumQ ©
Out [ 9 ] : Целые
100
dtype : int 64
In [10 ] : df . apply ( lambda x : x ** 2 )
Out [10 ] :

©

Целые
a
b
c
d

100
400
900
1600

In [ 11] : df ** 2
Out [ ll ] :

©

Целые

a
b
c
d

100
400
900
1600

О Атрибут index и объект Index .
© Атрибут columns и объект Index .
© Выбор значения, соответствующего индексу с .

© Выбор двух значений, соответствующих индексам а и d .
© Выбор второй и третьей строки по номерам индексов.
© Вычисление суммы значений столбца.
156

Глава 5

© Применение метода apply ( ) для вычисления квадратов значений векторизованным способом.

© Выполнение векторизованной операции непосредственно над объектом
DataFrame.

В отличие от массивов ndarray объект DataFrame можно увеличивать по

любым измерениям.
In [ 12 ] : df [ ' Вещественные ' ] = ( 1.5 , 2.5 , 3.5 , 4.5 )

О

In [ 13 ] : df
Out [ 13 ] :

а
Ь
с
d

Целые
10
20
30
40

Вещественные
1.5
2.5

3.5
4.5

In [ 14 ] : df [ ' Вещественные ' ] ©
0ut [l4 ] : a
1.5
b
2.5
c
3.5
d
4.5
Name : Вещественные , dtype : float 64

О Добавление нового столбца с вещественными числами, представленными в виде кортежа.

© Вывод содержимого столбца вместе с индексами.
В качестве источника данных для столбца может использоваться другой
объект DataFrame. В подобных случаях индексы согласуются автоматически.
In [ 15 ] : df [ ' Имена ' ] = pd . DataFrame ( [ ' Ив ' , ' Сандра ' , ' Лилли ' ,
' Генри ' ] , index = [ ' d ' , ' а ' , ' b ' , ' с ' ] )

О

In [ 16 ] : df
Out [ 16 ] :

а
b
с
d

Целые
10
20
30
40

Вещественные
1.5
2.5
3.5
4.5

Имена
Сандра
Лилли
Генри
Ив

О Создание нового столбца на основе другого объекта DataFrame.
Анализ данных с помощью библиотеки pandas

157

Схожим образом работает операция присоединения данных. Однако слезамены индексов числодует опасаться неприятного побочного эффекта
вым диапазоном.
In [17]: df.append({'Целые': 108, ' Вещественные': 5.75,
' Имена': 'Джил '}, ignore index=True) О
Out[17]:
Целые Вещественные Имена



_

0
1
2
3
4

In [18]: df

10
20
30
40
100

1.50 Сандра
2.50 Лилли
3.50 Генри
Ив
4.50

5.75

Джил

df.append(pd.DataFrane({' Целые ': 100, ' Вещественные':
5.75, 'Имена': 'Джил '},
index=[' у ,])) ©

=

1

In [19]: df
Out[19]:
а
b

Целые Вещественные
10
20
30
40
100

с
d
У

In [28]: df

Имена

1.50 Сандра
2.50 Лилли
3.50 Генри
4.50
Ив
5.75
Джил

= df.append(pd.DataFrame({' Имена': 'Лиз'}, index=['z',]),
sort=False)

©

In [21]: df
Out[21]:
а

Целые Вещественные

с
d
У

1.50 Сандра
2.50 Лилли
3.50 Генри
Ив
4.50

Z

NaN

Лиз

Ь

5.75
NaN

In [22]: df.dtypes ©
float64
Out[22]: Целые
Вещественные float64
object
Имена
dtype: object
158

Имена

10.0
20.0
30.0
40.0
100.0

Глава 5

Джил

О Добавление новой записи
безвозвратно теряются.

в виде словаря, при этом исходные индексы

новой записи на основе объекта DataFrame, включающего
индексную информацию. Исходные индексы остаются неизменными.

© Добавление

© Добавление неполной записи приводит к появлению значений NaN.
© Просмотр типов столбцов. Это напоминает работу со структурированным объектом ndarray.

Несмотря на появление пустых значений, методы объекта DataFrame
по -прежнему работают

.

I n [ 2 3 ] : df [ [ ' Целые ' ,
Out [ 2 3 ] : Целые
Вещественные

' Вещественные ' ] ] . mean ( ) О
40.00
3.55

dtype : float 64
I n [ 2 4 ] : df [ [ ' Целые ' ,
Out [ 2 4 ] : Целые
Вещественные

' Вещественные ' ] ] . std ( )

©

35.355339
1.662077

dtype : float 64

О Вычисление среднего для двух столбцов (строки со значениями NaN игнорируются).

© Вычисление среднеквадратического отклонения для двух столбцов (строки со значениями NaN игнорируются).

Расширенные возможности класса DataFrame
Ниже рассматривается пример, в котором используется объект ndarray,
содержащий массив случайных чисел с нормальным распределением. Это по зволит нам исследовать дополнительные возможности библиотеки pandas, в
частности функцию Datetimelndex ( ), предназначенную для обработки временных рядов.
I n [ 2 5 ] : import numpy as np
I n [ 2 6 ] : np . random . seed ( 100 )

I n [ 2 7 ] : a = np . random . standard _normal ( ( 9 , 4 ) )
In [ 28]: a

Анализ данных с помощью библиотеки pandas

159

Out [ 28 ] : аггау ( [[ - 1.74976547 ,
[ 0.98132079 ,
[ - 0.18949583 ,
[ - 0.58359505 ,
[ - 0.53128038 ,
[ 1.61898166 ,
[ 0.18451869 ,
[ - 0.32623806 ,
[ - 0.75635231 ,

0.3426804 , 1.1530358 , - 0.25243604 ] ,
0.51421884 , 0.22117967 , - 1.07004333 ] ,
0.25500144 , - 0.45802699 , 0.43516349 ] ,
0.81684707 , 0.67272081 , - 0.10441114 ] ,
1.02973269 , - 0.43813562 , - 1.11831825 ] ,
1.54160517 , - 0.25187914 , - 0.84243574 ] ,
0.9370822 , 0.73100034 , 1.36155613 ] ,
0.05567601 , 0.22239961 , - 1.443217 ] ,
0.81645401 , 0.75044476 , - 0.45594693 ]] )

Объект Data Frame можно создавать по- разному, в том числе на основе объекта ndarray . Такое решение удобно, поскольку структура исходного массива
не меняется, к нему лишь добавляется метаинформация ( индексы ). Это типич ный способ импорта числовых значений в финансовых и научных приложениях.
In [ 29 ] : df

=

О

pd . DataFrame ( a )

In [ 30 ] : df
Out [ 30 ] :

2

3

- 1.749765 0.342680 1.153036
0.981321 0.514219 0.221180
- 0.189496 0.255001 - 0.458027
- 0.583595 0.816847 0.672721
- 0.531280 1.029733 - 0.438136
1.618982 1.541605 - 0.251879
0.184519 0.937082 0.731000
- 0.326238 0.055676 0.222400
- 0.756352 0.816454 0.750445

- 0.252436
- 1.070043
0.435163
- 0.104411
- 1.118318
- 0.842436

0
0
1
2
3
4
5
6
7
8

1

1.361556

- 1.443217
- 0.455947

О Создание объекта DataFrame на основе объекта ndarray .
В табл. 5.1 перечислены параметры, передаваемые функции DataFrame ( ) .
Таблица 5.1. Параметры функции DataFrane ( )
Параметр

Формат

Описание

data

ndarray /dict / DataFrame

Данные для объекта DataFrame . Объект diet может
включать объекты Series , ndarray и l i s t

index
columns
dtype

Index / массив
Index / массив
dtype, по умолчанию None

Список имен столбцов; по умолчанию — range ( п )

copy

bool , по умолчанию None

160

Глава 5

Список индексов; по умолчанию — range ( л )

Тип данных столбцов; по умолчанию подбирается
автоматически
Флаг копирования данных из источника

Термином “ массив ” здесь обозначается структура, подобная объекту ndarray,
например список. Index это экземпляр класса Index библиотеки pandas.
Как было показано выше, имена столбцов объекта DataFrame можно
непосредственно задать в виде списка, содержащего нужное число элементов.
Другими словами, задавать или менять атрибуты объекта DataFrame совсем
не сложно.



In [ 31] : df.columns = [' 1', ' 2', ' 3 ' , ' 4 ]

©

In [ 32 ] : df
Out[ 32 ] :
1
0 - 1.749765
1 0.981321
2 - 0.189496
3 - 0.583595
4 - 0.531280
1.618982
5
0.184519
6
7 - 0.326238
8 - 0.756352

2

3

4

0.342680
0.514219
0.255001
0.816847
1.029733
1.541605
0.937082
0.055676
0.816454

1.153036
0.221180
- 0.458027
0.672721
- 0.438136
- 0.251879
0.731000
0.222400
0.750445

-0.252436
- 1.070043
0.435163

- 0.104411
- 1.118318
- 0.842436
1.361556

- 1.443217
- 0.455947

In [ 33 ] : df[' 2 '].meanQ ©
Out[33]: 0.7010330941456459

О Задание имен столбцов в виде списка.
© Теперь столбец можно легко выбрать по имени.
Для эффективной работы с временными рядами необходимо иметь возможность использовать значения даты / времени в качестве индексов. В би блиотеке pandas эта задача решается очень легко. Предположим, что в нашей
таблице, состоящей из девяти строк и четырех столбцов, записи соответствуют датам конца месяца, начиная с января 2019 года. Соответствующий объект
Datetimelndex генерируется с помощью метода date range ( ) .

_

In [ 34 ] : dates = pd.date _ range('2019 - 1 - 1', periods =9, freq =' M ' )

©

In [ 35 ] : dates
Out [ 35 ] : Datetimelndex(['2019 - 01 - 31 ', '2019 - 02 - 28', ' 2019 - 03 - 31 ',
'2019 - 04- 30', '2019 - 05 - 31', '2019 - 06 - 30',
'2019 - 07 - 31', '2019 - 08 - 31', '2019 - 09 - 30'],
dtype= ' datetime64[ns]', freq =' M ' )

О Создание объекта Datetimelndex.
Анализ данных с помощью библиотеки pandas

161

_

Параметры метода date range ( ) описаны в табл. 5.2.

_

Таблица 5.2. Параметры метода date range ( )
Параметр

Формат

start

string / datetime

Начальная дата диапазона

end

string / datetime

Конечная дата диапазона

periods

integer / None

Количество периодов ( если параметр start или end
равен None )

freq

string / DateOffset

Строка , задающая длину периода ( например, 5 D означает
5 дней )

tz

string / None

Название часового пояса в случае локализованного индекса

Описание

Флаг нормализации дат start и end , чтобы отсчет шел
с полуночи
string , по умолчанию None Название создаваемого индекса

normalize bool , по умолчанию None
name

В следующем примере только что созданный объект Datetimelndex применяется к имеющемуся массиву.
In [ 36 ] : df . index

= dates

In [ 37 ] : df
Out [ 37 ] :

2019 - 06 - 30
2019 - 07 - 31
2019 - 08 - 31
2019 - 09 - 30
01 - 31
02 - 28
03 - 31
04 - 30
2019 05 - 31

2019
2019
2019
2019

1

2

- 1.749765

0.342680
0.514219
0.255001
0.816847
1.029733
1.541605
0.937082
0.055676
0.816454

0.981321

- 0.189496
- 0.583595
- 0.531280
1.618982
0.184519

- 0.326238
- 0.756352

3
1.153036
0.221180
- 0.458027
0.672721
- 0.438136
- 0.251879
0.731000
0.222400
0.750445

4

- 0.252436
- 1.070043
0.435163

- 0.104411
- 1.118318
- 0.842436
1.361556

- 1.443217
- 0.455947

_

При создании объектов Datetimelndex с помощью метода date range ( )
можно использовать различные значения аргумента f req ( табл. 5.3) .

_

Таблица 5.3. Значения параметра f req метода date range ( )
Единица измерения

Описание

В

Банковский день

С

Назначаемый банковский день ( экспериментальная возможность)

162

Глава 5

Окончание табл. 5.3
Единица измерения

Описание

D

Календарный день

W

Неделя

М

Конец месяца

ВМ

Конец финансового месяца

MS

Начало месяца

BMS

Начало финансового месяца

Q

Конец квартала

BQ

Конец финансового квартала

QS

Начало квартала

BQS

Начало финансового квартала

А

Конец года

ВА

Конец финансового года

AS

Начало года

BAS

Начало финансового года

Н

Час

т

Минута

S

Секунда

L

Миллисекунда

и

Микросекунда

В некоторых случаях может потребоваться получить доступ к исходным
данным массива ndarray . Для этого предусмотрен атрибут values .
In [ 38 ] : df . values
Out [ 3S ] : аггау ( [[ - 1.74976547 ,
[ 0.98132079 ,
[ - 0.18949583 ,
[ - 0.58359505 ,
[ - 0.53128038 ,
[ 1.61898166 ,
[ 0.18451869 ,
[ - 0.32623806 ,
[ - 0.75635231,

0.3426804 , 1.1530358
0.51421884 , 0.22117967
0.25500144 , - 0.45802699
0.81684707 , 0.67272081
1.02973269 , - 0.43813562
1.54160517 , - 0.25187914
0.9370822 , 0.73100034
0.05567601, 0.22239961
0.81645401, 0.75044476

- 0.25243604 ] ,
- 1.07004333 ] ,
0.43516349 ] ,
- 0.10441114 ] ,
1.11831825 ] ,
- 0.84243574 ] ,
1.36155613 ] ,
- 1.443217 ] ,

-

- 0.45594693 ]] )

In [ 39 ] : np . array ( df )

Анализ данных с помощью библиотеки pandas

163

0ut[39]: array([[ - l.74976547,
[ 0.98132079,
[- 0.18949583,
[- 0.58359505,
[ - 0.53128038,
[ 1.61898166,
[ 0.18451869,
[ - 0.32623806,
[ - 0.75635231,

0.3426804 ,
0.51421884,
0.25500144,
0.81684707,
1.02973269,
1.54160517,
0.9370822 ,
0.05567601,
0.81645401,

- 0.25243604],
- 1.07004333],
0.43516349],
- 0.10441114],
- 1.11831825],
- 0.84243574],
1.36155613],
- 1.443217 ],
- 0.45594693]])

1.1530358
0.22117967
- 0.45802699
0.67272081
- 0.43813562
- 0.25187914
0.73100034
0.22239961
0.75044476

Объекты ndarray и DataFrane

и

(

Можно не только создать объект DataFrane из объекта ndarray,
но и выполнить обратную операцию создать объект ndarray из
{ объекта DataFrane. Для этого воспользуйтесь атрибутом values
класса DataFrane или методом пр.аггауО библиотеки NumPy.



Основные аналитические возможности
Подобно классу ndarray в NumPy, класс DataFrane библиотеки pandas содержит множество удобных методов. Для начала рассмотрим методы info()
и describeQ.
In [40]: df.infoQ О


:

'

л

Рис. 5.2. Гистограмма, построенная на основе данных объекта DataFrane
170

Глава 5

Класс Series
Помимо класса DataFrame в библиотеке pandas имеется другой важный
класс: Series. Он применяется для хранения данных, представленных единственным столбцом. В этом смысле класс Series можно рассматривать как

частный случай класса DataFrame. Объект Series будет получен при выборе
отдельного столбца из табличной структуры объекта DataFrame.
I n [55]: type(df)
Out [ 55 ] : pandas . core . frame . DataFrame
I n [56]: S = pd . Series ( np . linspace ( 0 , 15, 7), name= ' series ' )

I n [ 57 ] : S
0.0
0ut [ 57 ] : 0
1
2.5
5.0
2
7.5
3
10.0
4
5
12.5
6
15.0
Name : series , dtype : float 64
I n [58]: type(S)
Out [ 58 ] : pandas . core . series . Series
I n [ 59 ] : s = df [ ' 1 ' ]
I n [60]: s
0u t [ 6 G ] : 2019 - 01- 31 - 1.749765
2019 - 02 - 28
0.981321
2019 - 03 - 31 - 0.189496
2019 - 04 - 30 - 0.583595
2019 - 05 - 31 - 0.531280
2019 - 06 - 30
1.618982
2019 - 07 - 31
0.184519
2019 - 08 - 31 - 0.326238
2019 - 09 - 30 - 0.756352
Freq : M , Name : 1, dtype : float 64
I n [61]: type(s)
0u t [ 61] : pandas . core . series . Series

Анализ данных с помощью библиотеки pandas

171

Все основные методы объекта DataFrame применимы и к объекту Series .
В качестве иллюстрации рассмотрим применение методов meanQ и plot ( )
( рис . 5.3 ) .
In [62]: s.meanO
Out[62]: 0.15021177307319458

-

In [63]: s.plot(lw=2.0, figsize=(10, 6));
1. :

.)

1.0

0.5

0.0

0.5

1.0

1.5

Mar

Feb

Jan

Apr

May

Jun

Jul

Aug

Sep

2019

Рис . 5.3. График данных, хранящихся в объекте Series

Группирование данных
В библиотеке pandas поддерживаются гибкие возможности группирова ния данных подобно тому, как это реализовано в SQL и сводных таблицах
Microsoft Excel. Например, в объект DataFrame можно добавить столбец, обо значающий квартал , по которому будет выполняться группировка.
In [64]: df[ 1 Квартал 1 ] = [’ Ql' , 'Ql', ' Ql '
1
Q2 ' , 'Q31 , ' Q3 '

' Q2\ ' Q2\

' Q31]

df
Out[64]:
4

Квартал

1.749765 0.342680 1.153036 0.252436
0.981321 0.514219 0.221180 1.070043

Ql
Ql

1

2019 - 01- 31
2019 02 28

- -

172

Глава 5

-

2

3

-

-

- -

2019-03 31
2019 04 30
2019 -05 - 31
2019-06 - 30
2019 07- 31
2019 -08- 31
2019 09 30

- -

-0.189496
-0.583595
-0.531280

-

1.618982
0.184519
0.326238
0.756352

0.255001
0.816847
1.029733
1.541605
0.937082
0.055676
0.816454

- 0.458027
0.672721
- 0.438136
0.251879
0.731000
0.222400
0.750445

-

-

0.435163
0.104411
1.118318
0.842436
1.361556
1.443217
0.455947

Ql

Q2
Q2
Q2
Q3
Q3
Q3

Вот как сгруппировать данные по кварталам и вычислить статистику по
ним.

In [65]: groups

=

О

df.groupby(' Квартал )
1

In [66]: groups.size()
0ut[66]: Квартал
3
Q1
3
Q2
з
Q3
dtype: int64

©

In [67]: groups.mean()
0ut[67]:

©

Квартал
Q1
Q2
Q3

1

2

3

4

-0.319314
0.168035
-0.299357

0.370634
1.129395
0.603071

0.305396

-0.005765

-0.295772
-0.688388
-0.179203

In [68]: groups.max()
0ut [ 68 ] :

0.567948

©

»

3
1
2
Квартал
0.981321 0.514219 1.153036
Q1
1.618982 1.541605 0.672721
Q2
0.184519 0.937082 0.750445
Q3

In [69]: groups.aggregate([nin, max]).round(2)
0ut[69]:
3
2
1

min

max

min

max

min

4

0.435163

-0.104411
1.361556

©
4

max

max

min

Квартал
1.75 0.98 0.26 0.51 0.46 1.15 1.07 0.44
Ql
0.58 1.62 0.82 1.54 0.44 0.67 1.12 0.10
Q2
0.76 0.18 0.06 0.94 0.22 0.75 1.44 1.36
Q3

--

--

-

-

Анализ данных с помощью библиотеки pandas

173

О Группировка по столбцу Квартал .
© Подсчет количества строк в каждой группе.

© Среднее по каждому столбцу.

© Максимальное значение по каждому столбцу.
© Минимальное и максимальное значения по каждому столбцу.
Группировку можно выполнять сразу по нескольким столбцам. Для примера добавим в таблицу еще один столбец, в котором каждый месяц индекса
будет помечаться как четный или нечетный.
In [70]: df[' Чет/нечет'] = ['Нечет ' , ' Чет ' , ' Нечет' , 'Чет', 'Нечет ' ,
' Чет ' , 'Нечет ' , ' Чет ' , 'Нечет']
In [71]: groups

=

df.groupby(['Квартал ' , ' Чет/нечет '])

In [72]: groups.sizeQ
0ut[72]: Квартал Чет/нечет
Нечет
Q1

Q2
Q3

2
1
1
2
2
1

Чет
Нечет
Чет
Нечет

Чет
dtype: int64

In [73]: groups[[' 1', ' 4']].aggregate([sum, np.mean])
Out[73]:
4

1
Квартал
Ql

Q2
Q3

174

Глава 5

Чет / нечет
Нечет
Чет
Нечет
Чет
Нечет
Чет

sun

mean

- 1.939261
0.981321
- 0.531280

- 0.969631

1.035387
- 0.571834
- 0.326238

0.981321
- 0.531280
0.517693
- 0.285917
- 0.326238

sun
0.182727

nean
0.091364

- 1.070043 - 1.070043
- 1.118318
- 0.946847

- 1.118318
- 0.473423

0.905609

0.452805

- 1.443217 - 1.443217

Сложные операции извлечения данных
Чаще всего данные извлекаются согласно условиям, накладываемым на
значения столбцов. В качестве примера рассмотрим следующий набор данных

.

In [74]: data = np.random.standard _ normal((10, 2))

О

In [75]: df = pd.DataFrame(data, columns=[' x', ' у '])

©

In [76]: df.infoQ ©

Rangelndex: 10 entries, 0 to 9
Data columns (total 2 columns):
x
10 non - null float64
10 non - null float64
у
dtypes: float64(2)
memory usage: 240.0 bytes
In [77]: df.headQ
0ut[77]:
0
1
2
3
4

©
x

у

1.189622
- 1.356399
- 0.544439
0.007315
1.299748

- 1.690617
- 1.232435
- 0.668172
- 0.612939
- 1.733096

In [78]: df.tail()
Out[78]:

©
X

5
б
7
8
9

О

- 0.983310
- 1.613579

- 1.188018
- 0.940046
0.108863

У
0.357508
1.470714
- 0.549746
- 0.827932
0.507810

Массив ndarray случайных чисел с нормальным распределением.

© Объект DataFrame, содержащий тот же набор случайных чисел.

© Первые пять строк, возвращаемые методом head ( ).
© Последние пять строк, возвращаемые методом tail( ).

Анализ данных с помощью библиотеки pandas

175

В следующем примере показано, как применять логические операции и
операции сравнения к значениям двух столбцов.

О

In [ 79 ] : d f [ ' х ' ] > 0.5
True
0ut [ 79 ] : 0
1 False
False
2
False
3
4
True
False
5
6

False

False
7
False
8
9
False
Name : x , dtype: bool
1
In [ 80] : ( d f [ ' x ' ] > 0) & ( d f [ ' y ] < 0 )
True
Out [ 80 ] : 0
1 False
False
2
True
3
4
True
False
5
False
6
False
7
False
8
9
False

©

dtype: bool
In [ 81] : ( d f [ ' x ' ] > 0)
True
Out [ 81 ] : 0
True
1
2
True
3
True
True
4
False
5
False
6
7
True
8
True
9
True
dtype: bool

I

( d f [ ' y ' ] < 0)

©

О Превышают ли элементы столбца х значение 0 . 5 ?

176

Глава 5

© Являются ли значения столбца х положительными и значения столбца у
отрицательными ?

© Являются ли значения столбца х положительными или значения столбца
у отрицательными ?

Полученные в предыдущем примере объекты Series с булевыми значени ями удобно использовать в качестве критериев отбора данных. Альтернативным решением будет извлечение данных с помощью метода query ( ) , в котором критерий отбора задается в виде строки.

О

I n [8 2 ] : d f [ d f [ ' х ' ] > 0 ]
O u t[8 2 ] :

х
0 1.189622
3 0.007315
4 1.299748
9 0.108863

у

-1.690617
- 0.612939
-1.733096
0.507810

I n [ 8 3] : d f . q u e r y ( ' x > 0 ’ )
O u t [8 3] :

О

х

у

0 1.189622
3 0.007315
4 1.299748
9 0.108863

- 1.690617
- 0.612939
- 1.733096
0.507810

I n [8 4 ]: d f [ ( d f [ 1 x ’ ] > 0 ) & ( d f [ ’ y ’ ] < 0 ) ]
0 u t [S 4 ] :
x
У
0 1.189622 - 1.690617
0.612939
3 0.007315
4 1.299748 - 1.733096

©

-

I n [ 8 5]: d f . q u e r y ( 1 x > 0 & У < O ’ )
0u t [ 8 5 ] :
у
x
0 1.189622 - 1.690617
3 0.007315 - 0.612939
4 1.299748 - 1.733096
I n [ 8 6 ]: d f [ ( d f . x > 0 )
0u t[ 8 6 ]:

.

(df y

<

0) ]

©

©

Анализ данных с помощью библиотеки pandas

177

у
1.690617
- 1.232435
- 0.668172
-0.612939
- 1.733096
- 0.549746
- 0.827932
0.507810

X

0
1
2
3
4
7
8
9

1.189622
- 1.356399
- 0.544439
0.007315
1.299748
- 1.188018
- 0.940046
0.108863

О Строки, в которых значения столбца х больше 0.

© Строки, в которых значения столбца х положительные и значения столбца у отрицательные.

© Строки, в которых значения столбца х положительные или значения

столбца у отрицательные ( в данном случае доступ к столбцам осущест вляется через соответствующие атрибуты ).

Операторы сравнения могут также применяться сразу ко всему объекту
DataFrame.

In [ 87 ]: df
Out[87]:

О

0

>

X

У

True False
False False
False False
True False
True False
True
5 False
True
6 False

0
1
2
3
4

7
8
9

False False
False False
True

df [ df

> 8]

True

©

Out [ 88 ] :
X
У
NaN
0 1.189622
NaN
NaN
1
NaN
2
NaN
NaN
3 0.007315
4 1.299748
NaN
NaN 0.357508
5

178

Глава 5

6
7
8
9

NaN 1.470714
NaN
NaN
NaN
NaN
0.108863 0.507810

О Определение того, какие из значений объекта Data Frame положительные.
Q

Выбор положительных чисел и замена всех остальных чисел значением
NaN.

Конкатенация, соединение и слияние данных
В этом разделе мы рассмотрим различные подходы к объединению двух
простых наборов данных, хранящихся в объектах DataFrame. Во всех примерах используются следующие наборы .
In [ 89 ] : dfl

.

pd DataFrame ( [ ' 100 ' , ' 200 ' , ' 300 ' , ' 400 ' ] ,
index = [ ' a 1 , ' b ' , V , ’ d ' ] ,
columns= [ 1 A 1 , ] )

=

In [ 90 ] : dfl
Out [ 90 ] :
A

a 100
b 200

c 300
d
In [ 91 ] : df 2

400
pd . DataFrame ( [ ' 200 ' , ' 150 ' , ' 50 ' ] ,
i n d e x=[ ' f ' , ' b ' , ' d ' ] ,
columns = [ 1 В ' , ] )

=

In [ 92 ] : df 2
0 ut [ 92 ] :
В

f 200
b 150
d 50

Конкатенация



Конкатенация, или присоединение,
это операция добавления строк из
одного объекта DataFrame в другой. Такая операция реализуется с помощью
Анализ данных с помощью библиотеки pandas

179

.

метода append ( ) или функции pd concatQ. Основное внимание следует об ратить на обработку индексов.

.

In [ 93 ] : dfl append( df 2, sort=False )
Out [ 93 ] :
А
В
а 100 NaN
b 200 NaN
с 300 NaN

d
f
b
d

400
NaN
NaN
NaN

.

О

NaN
200
150
50

_

In [ 94 ] : dfl append( df 2, ignore index=True, sort=False )
Out [ 94 ] :
В
A
0 100 NaN
1 200 NaN
2 300 NaN
3 400 NaN
4 NaN 200
5 NaN 150
50
6 NaN

.

In [ 95 ] : pd concat ( ( dfl, df 2 ) , sort =False )
0ut [ 95 ]:
В
A
a 100 NaN
b 200 NaN
c 300 NaN
d 400 NaN
f NaN 200
b NaN 150
d NaN
50

.

_

©

©

In [ 96 ] : pd concat ( ( dfl, df 2 ) , ignore index=True, sort=False )
Out [ 96 ] :
A
В
0 100 NaN
1 200 NaN
2 300 NaN
3 400 NaN
4 NaN 200

180

Глава 5

©

5
6

NaN
NaN

150
50

О Добавление содержимого объекта df 2 в объект df1 в виде новых строк.

© То же самое, но без учета индексов.
© Тот же результат, что и в п. 1.
© Тот же результат, что и в п. 2.

Соединение
При соединении двух наборов направление копирования записей имеет значение. По умолчанию используются только индексы первого объекта
DataFrame. Такого рода операция называется левым соединением (left join ).
In [ 97 ] : dfl . join ( df 2 )
0 ut [ 97 ] :
A
В
a 100 NaN
b 200 150
c 300 NaN
d 400 50

.

In [ 98 ] : df 2 join ( dfl )
0 ut [ 98 ] :
A
В
f 200 NaN
b 150 200
d 50 400

©

©

О Применяются индексы объекта dfl.

© Применяются индексы объекта df 2.
Метод join ( ) поддерживает четыре способа соединения, которые различа ются способом обработки индексов и приводят к разным результатам.

.

In [ 99 ] : dfl join ( df 2 , how = ' left ' )
0 ut [ 99 ] :
A
В
a 100 NaN
b 200 150
c 300 NaN
d 400 50

©

Анализ данных с помощью библиотеки pandas

181

.

©

.

©

I n [ 108 ] : d f l j o i n ( df 2, how= ' r i g h t ' )
Out [ 100 ] :
A
В
f NaN 200
b 200 150
d 400
50
I n [ 101 ] : d f l j o i n ( df 2, how= ' inner 1 )

Out [ 101 ] :

b
d

A
200
400

В
150
50

.

I n [ 102 ] : d f l j o i n ( df 2, how= ' outer ' )
Out [ 182 ] :
В
A
a 100 NaN
b 200 150
c 300 NaN
d 400
50
f NaN 200

©

О Левое соединение (операция по умолчанию).

© Правое соединение равнозначно соединению объектов DataFrame в обратном порядке.

© При внутреннем соединении отбираются только строки с совпадающими
индексами.

© При внешнем соединении сохраняются все индексы.
Допускается соединение с пустым объектом DataFrame. В таком случае
столбцы создаются в порядке указания, что напоминает левое соединение.

.

I n [ 103 ] : d f = pd DataFrame ( )
I n [ 104 ] : df [ ' A ' ] = d f l[ ' A ' ]
I n [ 105 ] : df
Out [ 105 ] :
A

a
b

182

100
200

Глава 5

О

с

d

300
400

I n [ 106 ] : d f [ ' В ' ] = df 2

©

I n [ 107 ] : df
Out [ 107 ] :
В
NaN
150
NaN
50

A

a
b
c
d

100
200
300
400

О Объект df1 становится столбцом A.
© Объект df 2 становится столбцом В.
Использование словаря для объединения наборов данных приводит к
результату, аналогичному внешнему соединению, поскольку в таком случае
столбцы создаются одновременно.

.

I n [ 108 ] : df = pd DataFrame ( { ' A ' : d f l[ ' A ' ],

' В ' : d f 2[

1

В ' ]} )

О

I n [ 109 ] : df
Out [ 109 ] :

a

b
c
d
f

В
NaN
150
NaN
50
200

A
100
200
300
400
NaN

О Столбцы объектов DataFrame используются в качестве значений словаря.

Слияние
Если при соединении учитываются индексы объектов DataFrame, то при
слиянии учитываются совпадающие столбцы Чтобы продемонстрировать
это, добавим в оба исходных объекта DataFrame новый столбец С.

.

.

I n [ 110 ] : с = pd Series ( [ 250, 156, 50 ] , lndex= [ ' b ' ,
df1[ ' С ' ] = с
df 2 [ ' С ' ] = с

' d ' , ' с ' ])

Анализ данных с помощью библиотеки pandas

183

In [ 111 ] : dfl

Out[111]:
a
b
c
d

A
100
200
300
400

C
NaN
250.0
50.0
150.0

In [112]: df 2
Out [ 112 ] :

В

f 200
b 150
d 50

C
NaN
250.0
150.0

По умолчанию операция слияния выполняется по единственному общему
столбцу С. Но есть и другие варианты, например внешнее слияние.
In

[113]: pd . merge ( dfl , df 2 ) О

Out [ 113 ] :

А

0 100
1 200
2 400

С
NaN
250.0
150.0

В
200
150
50

[114]: pd . merge ( dfl , df 2 , on= ' C )
Out[114]:

In

А
0 100
1 200
2 400

С

NaN
250.0
150.0

О

В
200
150
50

In [ 115 ] : pd . merge ( dfl , df 2 , how = ' outer ' )

©

Out[115]:
0
1
2
3

А
100
200
300
400

С
NaN
250.0
50.0
150.0

В
200
150
NaN
50

О Слияние по столбцу С (вариант по умолчанию).

© Внешнее слияние, позволяющее сохранить все строки.

184

Глава 5

Другие способы слияния таблиц продемонстрированы ниже.
In [116]: pd.merge(dfl, df2, left on='A ' , right on= ' B ')
Out[116]:

_

_

_

_

_

_

C x
В Cy
0 200 250.0 200 NaN

A

In [117]: pd .merge(dfl, df2, left on='A 1 , right on='В', how= ' outer
0ut[117]:
C x
Cy
A
В
NaN NaN
NaN
0 100
1 200 250.0 200
NaN
2 300 50.0 NaN
NaN
NaN
3 400 150.0 NaN
NaN 150 250.0
4 NaN
NaN 50 150.0
5 NaN

_

_

_

_

In [118]: pd .merge(dfl, df2, left index=True, right index=True)
Out[118]:
C x
Cy
A
В
b 200 250.0 150 250.0
d 400 150.0 50 150.0

_

_

_

In [119]: pd.merge(dfl, df2, on= 1 C', left index=True)
0ut[119]:
C
A
В
NaN 200
f 100
b 200 250.0 150
d 400 150.0 50

_

In [120]: pd.merge(dfl, df2, on= C ' , right index=True)
Out[120]:
В
A
C
a 100
NaN 200
b 200 250.0 150
d 400 150.0 50
1

_

In [121]: pd.merge(dfl, df2, on='C', left index=True,
right index=True)
0ut[121]:
A
C
В
b 200 250.0 150
d 400 150.0 50

_

Анализ данных с помощью библиотеки pandas

185

Производительность вычислений
Из многочисленных примеров, приведенных в этой главе, прекрасно вид но, что в библиотеке pandas одну и ту же задачу можно решить несколькими
способами. В данном разделе мы сравним производительность поэлементного
суммирования двух столбцов. Сначала сгенерируем набор данных средствами

библиотеки NumPy.

.

_

.

In [ 122 ] : data = np random standard normal( ( 1080000, 2 ) )

.

О

In [ 123 ] : data nbytes
Out [ 123 ] : 16000000

.

In [ 124 ] : df = pd DataFrame ( data, columns=[ ' x ' ,
In [ 125 ] : df

.infoQ



Rangelndex : 1000000 entries, 0 to 999999
Data columns ( total 2 columns ) :
x
1000000 non - null float 64
у
1000000 non - null float 64
dtypes : float 64( 2 )
memory usage : 15.3 MB

О Объект ndarray, хранящий случайные числа.
© Объект DataFrame, хранящий случайные числа.
Далее замерим производительность операций суммирования.
In [ 126 ] : %time res = d f [ ' x ' ] + d f [ ' у ' ] ©
CPU times : user 7.35 ms, sys : 7.43 ms, total: 14.8 ms

Wall time: 7.48 ms
In [ 127 ] : res [ : 3 ]
0.387242
Out [ 127 ] : 0
1
0.969343
2
- 0.863159
dtype : float 64

-

.

In [ 128 ] : %time res = df sum ( axis=l) ©
CPU times : user 130 ms, sys : 30.6 ms, total: 161 ms
Wall time: 101 ms

186

Глава 5

In [ 129 ] : res[:3]
0.387242
Out [ 129 ] : 0
1 - 0.969343

-

2
0.863159
dtype: float64

res = df.values.sun(axis=l) ©
CPU tines: user 50.3 ns, sys: 2.75 ns, total: 53.1 ns
Wall tine: 27.9 ns

In [ 130 ] : %time

In [ 131] : res[:3]
Out [ 131] : array([ 0.3872424 , 0.96934273, 0.86315944])

-

-

= np.sum(df, axis=l) 0
CPU tines: user 127 ns, sys: 15.1 ns, total: 142 ns
Wall tine: 73.7 ns

In [ 132 ] : %tine res

In [ 133 ] : res[:3]
0.387242
Out [ 133 ] : 0

1
2

-0.969343
-0.863159

dtype: float64

= np.sun(df.values, axis-1) ©
CPU tines: user 49.3 ns, sys: 2.36 ns, total: 51.7 ns
Wall tine: 26.9 ns

In [ 134 ] : %tine res

In [ 135 ] : res [ : 3]
0ut [ 135 ]: array([ 0.3872424 , 0.96934273, 0.86315944])

-

-

О Непосредственная работа со столбцами ( объектами Series )



самый

быстрый подход.

© Вычисление сумм с помощью метода sum ( ) объекта DataFrame .
© Вычисление сумм с помощью метода sum ( ) объекта ndarray .

© Вычисление сумм путем применения функции np . sumQ

к объекту

DataFrame .

© Вычисление сумм путем применения функции np . sumQ к объекту
ndarray .

Анализ данных с помощью библиотеки pandas

187

Кроме того, значения можно суммировать с помощью методов evalQ и
applyO1.

In [136 ] : %time res = df.eval(' x + у ') О
CPU tines: user 25.5 ns, sys: 17.7 ns, total: 43.2 ns
Wall tine: 22.5 ns

In [ 137 ] : res[:3]
Out[137 ] : 0
0.387242
0.969343
1
0.863159
2
dtype: float64

-

In [ 138] : %tine res

= df.apply(lanbda row:
axis=l) ©

row['x'] + row['y '],

CPU tines: user 19.6 s, sys: 83.3 ns, total: 19.7 s
Wall tine: 19.9 s
In [ 139 ] : res[:3]
Out[ 139 ]: 0
0.387242
1
0.969343
2
0.863159
dtype: float64

--

О Метод evalQ применяется для вычисления сложных математических
выражений; имена столбцов извлекаются из выражения.

@

Метод apply ( ) выполняет суммирование построчно, а потому оказы вается самым медленным; это эквивалентно выполнению цикла по всем
строкам в коде Python.
Выбирайте с умом
В библиотеке pandas одну и ту же задачу можно решить множеством способов. Выбирая самый подходящий вариант, обращайте
внимание на производительность кода, если скорость работы приложения имеет для вас значение. Даже в нашем простом примере
скорость работы некоторых методов различается на порядок.

1

.

Применение метода eval ( ) требует наличия пакета numexpr ( https : / / питехрг readthedocs
io / en / latest / ).

188

Глава 5

.

Резюме



Библиотека pandas
мощный инструмент анализа данных и центральный пакет так называемого стека PyData. Ее класс DataFrame специально
предназначен для работы с табличными данными любого рода. Большин ство операций с такими объектами векторизовано, что позволяет не только
уменьшить объем кода, но и повысить его производительность. Кроме того,
библиотека pandas ( в отличие, например, от NumPy) позволяет работать с
неполными наборами данных. Как следствие, библиотека pandas и ее класс
DataFrame будут составлять основу многих решений, описанных в последующих главах, где мы будем рассматривать другие инструменты библиотеки по
мере необходимости .

Дополнительные ресурсы



Библиотека pandas
это проект с открытым исходным кодом, снабжен ный подробной документацией, которая доступна как в виде онлайн - ресурса,
так и в виде загружаемого PDF-файла 2 (http : / / pandas .pydata . org / ).
Как и в случае с NumPy, для знакомства с библиотекой pandas подойдут
следующие книги.



McKinney, Wes. Python for Data Analysis ( 2017, O’ Reilly) .

• VanderPlas, Jake. Python Data Science Handbook ( 2016, O’ Reilly).

2

На момент написания книги PDF- версия документации насчитывала более 2500 страниц.

Анализ данных с помощью библиотеки pandas

189

ГЛАВА 6

Объектно-ориентированное
программирование
Разработчики программного обеспечения должны
контролировать сложность, а не создавать ее.
Памела Зейв



Объектно-ориентированное программирование ( ООП )
одна из самых
популярных парадигм программирования . При правильном применении
ООП дает существенные преимущества по сравнению, например, с процедурным программированием. С точки зрения финансового моделирования и
реализации финансовых алгоритмов ООП оптимальный выбор. Тем не менее у этой парадигмы немало критиков, выражающих свой скептицизм как в
отношении отдельных ее аспектов, так и всей концепции в целом. В этой главе
мы будем придерживаться нейтральной точки зрения , считая ООП достаточ но эффективной, хотя и не единственно возможной методологией решения
большинства финансовых задач.
При знакомстве с ООП следует изучить основную терминологию.



Класс

Абстрактное определение конкретного типа объектов, например человека.
Объект
Экземпляр класса, например Сандра.

Атрибут
Характеристика класса ( атрибут класса ) или экземпляра класса ( атрибут объекта ). Например, характеристикой человека может быть пол
( мужской или женский ) или цвет глаз.
Метод
Действие, выполняемое классом или экземпляром класса, например

ходьба .

Параметры
Аргументы, передаваемые методам, например количество шагов.
Инициализация
Процесс создания конкретного объекта на основе абстрактного класса.

В переводе на язык Python простой класс HumanBeing , описывающий человека, может выглядеть так.
In [ 1 ] : class HurcanBeing ( object ) : О
def
i n i t ( s e l f , first _ name , eye color ) :

_
_
_
self first name = first name ©
self . eye _color = eye _color ©

©

,

self . position = 0 ©
def walk steps ( self , steps ) :
self . position + = steps ©

_

©

О Инструкция объявления класса.
© Специальный метод, вызываемый при создании экземпляра класса; ключевое слово self ссылается на текущий экземпляр класса.

© Атрибут имени, инициализируемый значением первого параметра.
© Атрибут цвета глаз, инициализируемый значением второго параметра.
© Атрибут позиции инициализируется значением 0.
© Определение метода, реализующего процесс ходьбы; в качестве параметра указывается количество шагов.

© Код, меняющий позицию на заданное число шагов.
На основе класса создаются объекты-экземпляры.
In [ 2 ] : Sandra

=

HumanBeing ( ' Сандра ' , ' голубой ' )

_

©

In [ 3] : Sandra . first name
0ut [ 3 ] : ' Сандра '
In [ 4 ] : Sandra . position
Out [ 4 ] : 0

©

_

In [ 5 ] : Sandra . walk steps ( 5 )
In [ 6 ] : Sandra . position
0ut [ 6 ] : 5

192

Глава 6

©

©

©

О Создание экземпляра класса.
@

Получение значений атрибутов.

@

Вызов метода.

0 Получение обновленного значения атрибута position .
С точки зрения удобства применения ООП дает следующие преимущества.
Понятный способ мышления
Мы привыкли мыслить категориями реальных или абстрактных объектов, таких как автомобиль или финансовый инструмент. ООП идеально
подходит для моделирования таких объектов и их характеристик.

Упрощение кода
ООП помогает уменьшить сложность решаемых задач и реализуемых
алгоритмов, выполняя моделирование на уровне признаков.

Удобный программный интерфейс
ООП позволяет создавать удобные программные интерфейсы и в целом
получать более компактный код. Наглядными примерами служат классы ndarray библиотеки NumPy и DataFrame библиотеки pandas .
Естественный для Python способ моделирования
Как бы там ни было, ООП является основной парадигмой программи рования на Python. Отсюда проистекает выражение “ в Python все что
угодно является объектом” ООП позволяет создавать пользовательские
классы, экземпляры которых ведут себя подобно стандартным объектам Python.

В то же время ООП дает и ряд технических преимуществ .

Абстракция
Наличие атрибутов и методов позволяет создавать абстрактные, гибкие
модели объектов, позволяющие сконцентрироваться только на том, что
важно, отбросив все остальное. В финансовых приложениях это может
означать наличие универсального класса, моделирующего абстрактный
финансовый инструмент. Экземплярами такого класса будут конкретные финансовые продукты, разработанные, например, неким инвести ционным банком.
Модульность
Концепция ООП предусматривает разбивку кода на модули, подключа емые к приложениям по мере необходимости. Например, европейский
Объектно-ориентированное программирование

193

опцион на акции можно смоделировать с помощью одного класса или
для представления акций, другой
для самого
двух классов: один
опциона.





Наследование
это концепция ООП, согласно которой класс может
Наследование
заимствовать атрибуты и методы у другого класса. В финансовом при ложении от общего класса финансовых инструментов может быть унаследован класс деривативов, от него класс европейских опционов, а от
последнего класс европейского колл -опциона. Каждый класс наследует атрибуты и методы всех классов более высокого уровня.







Агрегирование
При агрегировании объект как минимум частично состоит из других
объектов, которые могут существовать независимо. Например, атрибутами класса европейского колл -опциона могут быть объекты, представ ляющие базовые акции и краткосрочные процентные ставки на их размещение. При этом данные объекты могут независимо использоваться
любыми другими объектами.
Композиция
Композиция подобна агрегированию, только в данном случаеобъекты
не являются независимыми. В качестве примера рассмотрим процентный своп с фиксированной и плавающей ставками. Объекты ставок
не могут существовать отдельно от объекта самого свопа.

Полиморфизм
Полиморфизм бывает разных видов. В частности, в Python широко при меняется неявная типизация ( duck typing ) , при которой стандартные
операции можно выполнять по отношению к самым разным классам
и их экземплярам, не располагая явной информацией об их типе. Для
класса финансовых инструментов это означает возможность вызова, на пример, метода get current _ price ( ) независимо от типа объекта ( акция, опцион, процентный своп ).

_

Инкапсуляция
Концепция инкапсуляции предполагает предоставление доступа к
данным класса только через открытые методы. Например, у класса,
моделирующего акцию, может быть атрибут current _ stock _ price .
Инкапсуляция означает, что доступ к данному атрибуту можно получить
только с помощью метода get _current _stock _ price ( ) , а сам атрибут

194

Глава 6

скрыт от пользователя. Такой подход позволяет избежать побочных эф фектов, связанных с непосредственным изменением атрибутов. В то же
время в Python возможности инкапсуляции ограничены.
На более высоком уровне преимущества ООП можно свести к двум общим
целям программной инженерии.

Повторное использование кода
Наследование и полиморфизм делают возможным повторное использование кода, что способствует повышению эффективности разработки
программного обеспечения. Кроме того, они упрощают последующее
сопровождение программного продукта.
Отсутствие избыточности
Вместе с тем ООП позволяет писать код, практически лишенный избы точности. Не приходится тратить усилия на повторную реализацию ал горитмов, плюс сокращается время, затрачиваемое на отладку, тестирование и сопровождение программ.
В главе рассматриваются следующие темы.

Обзор объектов Python
В этом разделе рассматриваются уже известные вам объекты Python в
контексте ООП.
Основные операции с классами Python
В этом разделе мы применим ООП в контексте работы с финансовыми
инструментами и инвестиционными портфелями.

Модель данных Python
В этом разделе мы поговорим о модели данных Python и важности
специальных методов.

Обзор объектов Python
Знакомство с ООП лучше всего начать с рассмотрения стандартных объектов, уже известных вам по предыдущим главам.

int
Начнем с объекта int ( целое число ) . Заметьте, что даже такой простой объект обладает всеми необходимыми функциональными характеристиками.

Объектно -ориентированное программирование

195

In [ 7 ] : n

=

О

5

©

In [ 8 ] : type ( n )
Out [ S ] : int

In [ 9 ] : n . numerator
Out [ 9 ] : 5

©

_

In [ 10 ] : n . bit length ( )
Out [10 ] : 3

In [11] : n + n

Outfit ] :

©

©

10

In [12 ] : 2 * n
Out [ 12 ] : 10
In [13] : n .
0ut[ 13 ] : 28

©

sizeof

()

©

0 Новый экземпляр n.
© Тип объекта.

© Атрибут.
© Метод.
© Применение оператора + ( сложение ).
© Применение оператора * ( умножение).
© Вызов специального метода

sizeof

( ) для определения объема зани -

маемой объектом памяти 1.

list



Списки тоже объекты, только по сравнению с целыми числами они поддерживают большее количество методов.
In [ 14 ] : I = [1, 2 , 3 , 4 ] О
In [ 15 ] : type ( l )
1

©

В Python специальные атрибуты и методы обрамляются символами двойного подчеркивания.
В частности, метод п . sizeof ( ) возвращает размер объекта п в байтах.

196

Глава 6

Out[ 15 ] : list

©

In [ 16 ] : l [ 0 ]
Out [ 16 ] : 1

0

In [ 17 ] : l . append ( lO )

In [ 18 ] : l + l ©
Out [ 18 ] : [ 1 , 2 , 3 , 4 , 10 , 1 , 2 , 3 , 4 , 10 ]
In [ 19 ] : 2 * 1 ©
0ut[ 19 ] : [ 1 , 2 , 3 , 4 , 10 , 1 , 2 , 3 , 4 , 10 ]
In [ 2 0 ] : sum ( l )
Out [ 20 ] : 20

©

In [ 21 ] : l . sizeof
Out [ 21 ] : 104

()

©

О Новый экземпляр l.

© Тип объекта.
© Выбор элемента по индексу.
© Метод.

© Применение оператора + ( сложение).
© Применение оператора * ( умножение).
© Вызов специального метода

sizeof

( ) для определения объема зани -

маемой объектом памяти.

ndarray



это стандартные объекты Python, то объект
Если списки и целые числа
ndarray заимствуется из дополнительной библиотеки NumPy.
In [ 22 ] : import numpy a s n p
In [ 2 3 ] : a

=

©

np . arange ( 16 ) . reshape ( ( 4 , 4 ) )

In [ 24 ] : a ©
0ut [ 24 ] : array ( [[ 0 ,
[ 4,

1,
5,

2,
6,

©

3] ,
7],

Объектно-ориентированное программирование

197

[ 8 , 9 , 10 , И ] ,
[ 12 , 13 , 14 , 15 ]] )
In [ 25 ] : type ( a ) ©
0ut [ 25 ] : nunpy . ndarray

О Импорт библиотеки NumPy.
© Новый экземпляр а .
© Тип объекта.
Несмотря на то что ndarray не относится к стандартным объектам, он во
многих случаях ведет себя подобно таким объектам благодаря принятой в
Python модели данных ( рассматривается далее).
In [ 26 ] : a . nbytes
0ut [ 26 ] : 128
In [ 27 ] : a . supiQ
0ut[ 27 ] : 120

©
©

In [ 28] : a . cumsum ( axis = 0 ) ©
0ut [ 28 ] : array ( [[ 0 , 1 , 2 , 3 ] ,
[ 4 , 6 , 8 , 10 ] ,
[ 12 , 15 , 18 , 21 ] ,
[ 24 , 28 , 32 , 36 ]] )
In [ 29 ] : a + a ©
Out [ 29 ] : array ( [ [ 0 , 2 , 4 , 6 ] ,
[ 8 , 10 , 12 , 14 ] ,
[ 16 , 18 , 20 , 22 ] ,
[ 24 , 26 , 28 , 30 ]] )
In [ 30 ] : 2 * a ©
Out[ 30 ] : array ( [[ 0 , 2 , 4 , 6 ] ,
[ 8 , 10 , 12 , 14 ] ,
[ 16 , 18 , 20 , 22 ] ,
[ 24 , 26 , 28 , 30 ]] )
In [ 31] : suni ( a ) ©
Out [ 31] : array ( [ 24 , 28 , 32 , 36 ] )
In [ 32 ] : np . sun ( a )
Out [ 32 ] : 120

198

Глава 6

©

.

In [ 33 ] : a
sizeof
0ut [ 33 ] : 112

()

©

О Атрибут.

© Метод (с агрегирование данных).
© Метод (без агрегирования данных).
О Применение оператора + (сложение).

© Применение оператора * (умножение).

© Применение функции sum( ) из стандартной библиотеки Python.
© Применение универсальной функции np . sum ( ) из библиотеки NumPy.

© Вызов специального метода
маемой объектом памяти.

sizeof

( ) для определения объема зани-

DataFrame
Последним мы рассмотрим объект DataFrame библиотеки pandas, который
функционально напоминает объект ndarray. Для начала создадим объект
DataFrame на основе объекта ndarray.
In [ 34 ] : import pandas as pd

©

.

I n [ 35 ] : df = pd DataFrame ( a, columns=l i s t ( ' abed ' ) )

©

In [ 36 ] : type ( df ) ©
Out [ 36 ] : pandas core frame DataFrame

.

.

.

О Импорт библиотеки pandas.

© Новый экземпляр df.
© Тип объекта.
Теперь познакомимся с его атрибутами и методами, а также операциями,
которые он поддерживает.

.

In [ 37 ] : df columns
0ut [ 37 ] : Index ( [ ' a ' ,
I n [ 38 ] : df

. sumQ

О
' b ' , ' c ' , ' d ' ] , dtype= ' object ' )

©
Объектно-ориентированное программирование

199

Out [ 38 ] : a

24
28
32
36
dtype: int 64

b
c
d

.

In [ 39 ] : df cumsumQ ©
a b e d
Out [ 39 ] :
0
0 1 2 3
1 4 6 8 10
2 12 15 18 21
3 24 28 32 36
In [ 40 ] : df + df
a b
Out [ 40 ] :
0
0 2
1 8 10
2 16 18
3 24 26

I n [ 41] : 2
O u t [ 41] :
0
1

*

О
e d
4 6
12 14
20 22
28 30

df ©
a b e d

0 2 4 6
8 10 12 14
16 18 20 22
24 26 28 30

2
3

.

In [ 42 ] : np sun( df )
24
Out [ 42 ] : a
b
28
32
c

©

36
d
dtype : int 64

.

In [ 43 ] : df
Out [ 43 ] : 208

sizeof

()

©

О Атрибут.
Q

Метод (с агрегирование данных).

© Метод (без агрегирования данных).
О Применение оператора + (сложение).
200

Глава 6

© Применение оператора * (умножение).
© Применение универсальной функции np . sum ( ) из библиотеки NumPy.
© Вызов специального метода

slzeof

( ) для определения объема зани -

маемой объектом памяти.

Основные операции с классами Python
В этом разделе описан синтаксис применения классов в Python . Мы рассмотрим создание пользовательских классов, моделирующих объекты, которые невозможно смоделировать с помощью встроенных типов Python. В качестве примера мы выберем модель финансового инструмента.
В Python для создания нового класса достаточно двух строчек кода.

:
In [ 44 ] : class Flnanclallnstruroent(object)
pass ©
In [ 45 ] : fl

=

FlnanclallnstrupientQ

О

©

In [ 46 ] : type ( fi ) ©
0 ut [ 46 ] :
main . Flnanclallnstrupient

In [ 47 ] : fl ©
0ut[ 47 ] : < main

. Flnanclallnstrupient at 0 xll 6767278 >

In [ 48 ] : fl . str
0ut [ 4S ] : ' < Plain
In [ 49 ] : fl . price
In [ 50 ] : fl . price
Out [ 50 ] : 100

()

©

. Flnanclallnstrupient object at 0 xll 6767278 > '

=

100

©

©

О Определение класса2.

© Код класса; в данном случае это просто ключевое слово pass.
© Новый экземпляр класса с именем fl.
2

Имена классов рекомендуется задавать в “ горбатом” регистре (CamelCase). Впрочем, если
риск неоднозначности отсутствует, то можно использовать нижний регистр или даже “ змеи ный” регистр ( т.е. financlal _ lnstrument ) .

Объектно -ориентированное программирование

201

О Тип объекта.

0 Каждый объект Python обладает рядом “ специальных” атрибутов и методов (наследуемых от класса object ). В данном случае вызывается специ-

.

альный метод, возвращающий строковое представление объекта

©

Атрибуты данных, в отличие от специальных атрибутов, можно создавать на лету.

Отдельного внимания заслуживает специальный метод init ( ), вызываемый при создании каждого экземпляра объекта. В качестве аргументов ему
передается сам объект (представленный ключевым словом self ) и произволь ное число других параметров.
In [ 51] : class Financiallnstrunent ( object ) :
author = ’ Ив Хилпиш ' О

def

( self , symbol , price ) :
seif symbol = symbol ©
self price = price ©

init

.
.

In [ 52 ] : Financiallnstrument . author
Out [ 52 ] : ' Ив Хилпиш '

©

In [ 53 ] : aapl = Financiallnstrument ( ' AAPL ' , 108 )
In [ 54 ] : aapl . symbol
Out [ 54 ] : ' AAPL '

©

In [ 55 ] : aapl . author
0ut [ 5 S ] : ' Ив Хилпиш '

©

In [ 56 ] : aapl . price = 185
In [ 57 ] : aapl . price
Out [ 57 ] : 105

©

©

©

©

О Определение атрибута класса (наследуется каждым экземпляром).

0 Специальный метод

init

( ), вызываемый при создании объекта.

0 Определение атрибутов экземпляра (разные у каждого объекта).

© Новый экземпляр класса с именем aapl.

0 Получение атрибута экземпляра.
202

Глава 6

© Получение атрибута класса.

© Изменение атрибута экземпляра.
Стоимость финансовых инструментов постоянно меняется, а вот их тикеры обычно остаются неизменными. Чтобы продемонстрировать, как работает
механизм инкапсуляции, создадим определение класса с двумя новыми методами: get _ price ( ) и set _ price ( ) . Теперь код класса наследуется от предыдущего определения, а не от класса object .
In [ 58 ] : class Financiallnstrument ( Financiallnstrument ) : ©
def get __ price ( self ) : ©
return self . p r i c e ©
def set price ( self , price ) : ©
self price = price 0

_
.

In [ 59 ] : f i

= Financiallnstrument ( ' AAPL ' ,

In [ 60 ] : f i . get _ price ( )
Out [ 60 ] : 100

_

_

In [ 63 ] : f i . price
0ut [ 63 ] : 105

©

©

In [ 61 ] : f i . set price ( 105 )

In [ 62 ] : f i . get price ( )
Out [ 62 ] : 105

100 )

©

©

©

О Определение класса наследуется от предыдущей его версии.
© Определение метода get _ price ( ) .
© Определение метода set _ price ( )...

0 ...включающее обновление атрибута экземпляра, передаваемого в качестве параметра.

© Новый экземпляр f i , основанный на обновленном классе.
© Вызов метода get _ price ( ) для получения атрибута экземпляра.
© Обновление атрибута экземпляра с помощью метода set _ price ( ) .

© Прямой доступ к атрибуту экземпляра.
Объектно-ориентированное программирование

203

Инкапсуляция позволяет скрыть данные от пользователей класса. Частич но это реализуется с помощью методов доступа, но они не препятствуют
прямому обращению к атрибутам экземпляра. Здесь на помощь приходят за крытые атрибуты экземпляра, которые помечаются двумя начальными сим волами подчеркивания.
In [ 64 ] : class Financiallnstrument ( object ) :

irvit ( self , symbol, price ) :
self symbol = symbol
self
price = price О
def get _ pr ice( self ) :
price
return self
def set _price ( self , price ) :
price = price
self
def

.
.
_

.

.

In [ 65 ] : fi = Financiallnstrument ( ' AAPL ' , 100 )

.

In [ 66 ] : fi get _price ( )
0ut [ 66 ] : 100

.

In [ 67 ] : fi

price

©

©

AttributeError

Traceback (most recent call last )
in


> 1

.

fi

price

AttributeError :

©

' Financiallnstrument '
attribute '

.

price

._FinancialInstrument

price = 105

In [ 68 ] : fi _FinancialInstrument
Out [ 68 ] : 100
In [ 69 ] : fi

.

In [ 70 ] : fi set _price ( 100 )

object has no

price '

©

0

©

О Стоимость финансового инструмента хранится в закрытом атрибуте
экземпляра.

© Метод get _price ( ) возвращает значение закрытого атрибута.

© Попытка непосредственно обратиться к закрытому атрибуту приводит к
ошибке.

204

Глава 6

О Но если предварить имя атрибута именем класса с одиночным символом
подчеркивания, то прямой доступ все еще можно получить.

© Возврат цены к исходному значению.
Инкапсуляция в Python
Несмотря на то что инкапсуляцию в Python можно реализовать с
помощью закрытых атрибутов экземпляра и соответствующих методов доступа, полностью скрыть данные класса не представляется
возможным. Это в большей степени инженерный принцип, заложенный в основу языка, а не техническая особенность классов в Python.

Рассмотрим еще один класс, моделирующий портфельную позицию финансового инструмента На примере двух классов легко продемонстрировать
механизм агрегирования. Экземпляр класса PortfolioPosition получает эк земпляр класса Financiallnstrument в качестве своего атрибута. Добавив
соответствующий атрибут экземпляра, скажем, position size, можно будет
вычислить стоимость позиции

.

_

.

In [71]
: class

def

PortfolioPosition(object):
init (self, financial_instrument , position_ size ) :

self . position = financial_instrument О
self. position_ size = position_ size ©
def get position size(self):
return self. position_ size
def update position size(self , position_ size ) :
self . position_ size = position_ size
def get position vslue(self):
return self. position_ size * \
self . position . get _price ( ) ©

_

_

_

In [72]: pp

=

_

_

PortfolioPosition( fi, 10)

In [ 73 ] : pp . get_position_ size ( )
Out [ 73 ] : 10
In [ 74 ] : pp . get _position_value ( )
0ut [ 74 ] : 1000

©

In [ 75 ] : pp . position . get _price ( )
0ut [ 75 ] : 100

©

In [ 76 ] : pp . position . set _price (l05 )

©

Объектно-ориентированное программирование

205

_

_

In [ 77 ] : pp . get position value ( )
Out [ 77 ] : 1050

©

О Атрибут экземпляра, представленный экземпляром класса Financial Instrument.

© Закрытый атрибут экземпляра класса PortfolioPosition.
© Вычисление стоимости позиции на основе известных атрибутов.

© Если атрибутом экземпляра является объект, то его методы можно вызывать напрямую.

© Обновление стоимости финансового инструмента.
© Вычисление новой стоимости портфельной позиции на основе обновлен ной стоимости финансового инструмента.

Модель данных Python
В примерах предыдущего раздела демонстрировались определенные
аспекты модели данных Python ( https : / / docs . python . Org / 3 / reference /
datamodel . html ) . Она позволяет создавать собственные классы, согласованным образом взаимодействующие с базовыми конструкциями языка. Среди
прочего модель поддерживает следующие возможности:









циклические операции;

обработка коллекций;

доступ к атрибутам;
перегрузка операторов;
вызов методов и функций;

создание и удаление объектов;

строковое представление объектов ( например, для вывода информации
на экран );

• управление контекстом ( например, в блоках with ).
Модель данных Python настолько важна, что необходимо рассмотреть ее
особенности на конкретных примерах. Для этого мы создадим класс, опи сывающий трехэлементный вектор. Сначала реализуем специальный метод
init ( ) .
206

Глава 6

In [ 7 8 ] : class V e c t o r ( o b j e c t ) :
def
i n i t ( s e l f , x 0 , y = 0 , z =0 ) :

. =
. =
. =

self x
self у
self z
In [ 7 9 ] : v
In [ 80 ] : v
Out [ 80 ] : <

=

О
у О
z О

x

Vector ( l , 2 , 3 )

©
main

. Vector

=

О

©

at 0xll 67789e8>

О Три предварительно заданных атрибута экземпляра (координаты в трехмерном пространстве).
Q Новый экземпляр класса с именем v.

© Вывод строкового представления экземпляра, заданного по умолчанию.
Специальный метод герг
ковое представление класса.

( ) позволяет задать пользовательское стро-

In [ 81] : class Vector ( Vector ) :
repr (self ):
def
return ' Vector ( % r , % r , % r ) ' %
(self x, self y , self z )

.

In [ 8 2 ] : v

=

.

.

Vector ( l , 2 , 3 )

In [ 83 ] : v ©
0ut [ 83 ] : Vector ( l , 2 , 3 )

In [ 84 ] : print ( v ) ©
Vector ( l , 2 , 3 )

© Новый способ строкового представления класса.
В Python есть стандартные функции abs ( ) и boolQ , поведение которых
применительно к классу Vector можно настроить с помощью специальных
методов abs ( ) и bool ( ) .
In [ 85 ] : class Vector ( Vector ) :
def
abs (self ):
r e t u r n ( s e l f . x ** 2 + s e l f . y ** 2 +
s e l f z ** 2 ) ** 0 . 5 О
bool (self ):
def
return bool(abs( self ) )

.

Объектно-ориентированное программирование

207

In [ 86 ] : v

=

Vector ( l , 2 ,

1)

©

In [ 87 ] : abs ( v )
Out [ 87 ] : 2.449489742783178
In [ 88 ] : bool ( v )
0 ut [ 88 ] : True

In [ 89 ] : v

=

Vector ( )

©

In [ 90 ] : v ©
Out [ 90 ] : Vector ( 0 , 0 , 0 )
In [ 91] : abs ( v )
0 ut [ 91 ] : 0.0

In [ 92. ] : bool ( v )
0 ut [ 92 ] : False

О Возвращает евклидову норму по заданным значениям трех атрибутов.

© Новый объект Vector с ненулевыми значениями атрибутов.
© Новый объект Vector с нулевыми значениями атрибутов.
Как уже было неоднократно показано, операторы + и * применяются к
большинству объектов Python, а их поведение определяется специальными
методами add ( ) и mul ( ) .
In [ 93] : class Vector ( Vector ) :
def
add ( self , other ) :

x

=
=
=

.
.
.

.
.
.

self x + other x
self у + other у
z self z + other z
return Vector ( x , y , z )

у

def

In [ 94 ] : v

=

ml ( self , scalar ) :
return Vector ( self . x * scalar ,
self . у * scalar ,
self . z * scalar )

Vector ( l , 2 , 3 )

In [ 95 ] : v + Vector ( 2 , 3 , 4 )

208

Глава 6

©

©

Out[95]: Vector(3, 5, 7)

In [96]: v * 2
Out[96]: Vector(2, 4, 6)

О

В данном случае каждый специальный метод возвращает объект того же
типа.





Еще одна стандартная функция Python len( ) возвращает длину объекта, выраженную в виде количества содержащихся в нем элементов. При получении пользовательского объекта функция вызывает специальный метод
len ( ). Существует также специальный метод getitem ( ) , который
делает возможной индексацию через квадратные скобки.
In [97]: class Vector( Vector):
def len (self):
return 3 О
def getitem (self, i):
if i in [0, - 3]: return self.x
elif i in [1, - 2]: return seif.у
elif i in [2, 1]: return seif.z
else: raise IndexError( 1 Индекс за пределами
диапазона.')

-

In [98]: v = Vector(l, 2, 3)
In [99]: len(v)
0ut[99]: 3

In [100]: v[0]
Out[100]: 1
In [101]: v[ - 2]
Out[101]: 2
In [102]: v[3]

Traceback ( most recent call last)
IndexError
< ipython - input - 102 - f998c 57dccle> in < module>
- - - - > 1 v[3]


in getitem (self, i)
elif i in [1, - 2]: return self.у
Объектно -ориентированное программирование

209

8
> 9

elif i in [ 2 , -1] : return self . z
else : raise IndexError ( ' Индекс за пределами
диапазона . ' )

IndexError : Индекс за пределами диапазона .

О Все экземпляры класса Vector имеют длину 3.
Наконец, специальный метод iter ( ) определяет поведение объекта в
операциях циклического перебора его элементов. Объект, для которого определен такой метод, называется итератором. В частности, итераторами явля ются любые последовательности и контейнеры.
In [103 ] : class Vector ( Vector ) :
iter ( self ) :
def
for i in range ( len ( self ) ) :
yield self [ i ]
In [ 104 ] : v

=

Vector ( l , 2 , 3 )

In [105 ] : for i in range ( 3 ) :
print ( v [i ] ) О
1

О

2
3

In [106 ] : for coordinate in v :
print ( coordinate )
1
2
3

©
©

О Неявный перебор через индексы ( вызывается специальный метод
getitem

( )).

© Явное итерирование через интерфейс класса ( вызывается специальный
метод

(

I
*
210

iter

( ) ).

Расширение объектной модели Python
Модель данных Python позволяет определять классы, неявно взаи модействующие со стандартными операторами, функциями и т.п.
Такой подход делает Python невероятно гибким языком программирования, возможности которого легко расширяются за счет новых классов и типов объектов.

Глава б

Код класса Vector
Подытоживая все вышесказанное, запишем полное определение класса
Vector.

In [107]: class Vector(object):
def init (self, x=0, y=0, z=0):
self.x = x

self.у
self.z


=z

repr (self):
return ' Vector(%r, %r, %r)' %
(self.x, self.y , self.z)

def

def

abs (self):
return (self.x ** 2 + self.y ** 2 +
self.z ** 2) ** 0.5

bool (self):

def

return bool(abs(self))

def

add (self, other):
self.x + other.x
self.y + other.у
self.z + other.z
return Vector(x, y, z)

=
у =
z=
x

def

def

RUI (self, scalar):
return Vector(self.x * scalar,
self.y * scalar,
self.z * scalar)
len (self):
return 3

def

getitem (self, i):
if I in [0, - 3]: return self.x
elif i in [1, - 2]: return self.y
elif i in [2, - 1]: return self.z
else: raise IndexError(' Индекс за пределами

диапазона.')

def iter (self):
for i in range(len(self)):
yield self[i]
Объектно-ориентированное программирование

211

Резюме
В этой главе был дан обзор принципов объектно - ориентированного про граммирования на Python. ООП — одна из ключевых парадигм программирования, применяемых в Python. Она позволяет не только разрабатывать
сложные приложения, но и создавать собственные объекты, которые функ ционируют подобно стандартным объектам языка благодаря гибкой модели
данных. ООП предоставляет в распоряжение разработчиков мощные инстру менты, незаменимые в решении многих финансовых задач. В качестве примера можно привести пакет оценки деривативов, который мы разработаем в
части V. В этом случае ОПП оказывается единственной эффективной парадиг мой, позволяющей справиться с возникающими трудностями и требования ми к абстрагированию финансовых данных.

Дополнительные ресурсы
Следующие ресурсы представляют интерес для изучения ООП в целом и
объектно - ориентированного программирования на Python в частности:



конспект лекций по объектно - ориентированному программированию
(https: / / bit ly / 2ql_ JU0 S).



объектно - ориентированное программирование на Python (https : / /
bit ly / 2DKGZhB).

.

.

Великолепным источником информации по объектно -ориентированному
программированию на Python послужит следующая книга:

• Ramalho, Luciano. Fluent Python (2016, O’Reilly).

212

Глава 6

ЧАСТЫМ

Обработка и анализ финансовых

данных

Эта часть посвящена инструментам и пакетам, предназначенным для ра боты с финансовыми данными . Многие из рассматриваемых здесь тем ( на пример, визуализация данных) и библиотек ( таких, как Scikit -learn ) являются
фундаментальными с точки зрения применения науки о данных в Python. Но
мы сконцентрируемся на финансовых вычислениях.
Как и в части И, главы данной части посвящены конкретным темам и послужат справочным руководством, к которому читатели смогут обращаться в
случае необходимости.

• Глава 7 посвящена построению статических и интерактивных диаграмм
с помощью пакетов matplotlib и plotly .
• Глава 8 посвящена обработке временных рядов с помощью библиотеки
pandas .

• Глава 9 посвящена выполнению операций ввода - вывода.
• Глава 10 содержит рекомендации по повышению производительности
кода Python.
• Глава 11 посвящена математическому аппарату финансовых расчетов.
• Глава 12 посвящена реализации стохастических методов в Python.
• Глава 13 посвящена статистическим расчетам и методам машинного
обучения.

ГЛАВА 7

Визуализация данных
Используйте иллюстрацию. Она стоит тысячи слов.
Артур Брисбен

В этой главе рассматриваются средства визуализации данных, доступные в
пакетах natplotlib и plotly .
Несмотря на наличие других мощных библиотек визуализации, пакет
natplotlib ( www . natplotlib . org ) считается эталоном, предлагающим са мые надежные и эффективные решения. С одной стороны, он позволяет легко
строить стандартные графики; с другой стороны, он обеспечивает достаточ ную гибкость при настройке сложных диаграмм. Кроме того, он тесно интегрирован с библиотеками NumPy и pandas и поддерживает реализованные в
них структуры данных.
К сожалению, пакет natplotlib способен генерировать диаграммы только
в растровом формате ( например, PNG или JPEG ) . В то же время существуют
современные веб-технологии, такие как стандарт D3.js ( https : / / d 3 js . org ) ,

которые позволяют создавать интерактивные встраиваемые графические
объекты ( с поддержкой масштабирования отдельных областей). В Python за
построение такого рода диаграмм отвечает пакет plotly ( http : / / plot . ly ) .
Есть также небольшая вспомогательная библиотека Cufflinks, которая интегрирует пакет plotly с объектами DataFrane библиотеки pandas и поддерживает популярные финансовые диаграммы ( в частности, “ японские свечи” ) .
В главе рассматриваются следующие темы.
Статические двухмерные графики
В этом разделе мы познакомимся с библиотекой natplotlib и узнаем,
как строить типичные графики.
Статические трехмерные диаграммы
В этом разделе рассматриваются способы построения трехмерных диа грамм, применяемых в финансовых приложениях.

Интерактивные двухмерные диаграммы
В этом разделе вы узнаете, как с помощью пакетов plotly и Cufflinks
создавать интерактивные двухмерные диаграммы. Также будет рассмо трен процесс построения типичных финансовых диаграмм, применяемых, к примеру, в техническом анализе.
Данная глава не является исчерпывающим руководством по визуализации
в Python, а также пакетам matplotlib и plotly. Тем не менее в ней содержится
множество примеров применения этих пакетов в задачах финансового анализа. В последующих главах будут рассмотрены и другие примеры. В частности,
глава 8 будет посвящена визуализации финансовых временных рядов сред ствами библиотеки pandas.

Статические двухмерные графики
Прежде чем формировать выборку данных и строить графики, необходимо
импортировать соответствующие модули и задать ряд базовых настроек.
In [1]: import matplotlib as
In [2]: mpl . version
Out[2]: ' 3.0 . 0 '
In [3]
: import

mpi О

©

matplotlib.pyplot as pit

In [4]: pit . style . use ( ' seaborn ' )

©

©

In [ 5 ] : mpl . rcParams [ ' font . family ' ] =

' serif '

©

In [ 6 ] : %matplotlib inline

О Импорт пакета matplotlib, представленного аббревиатурой mpl.

© Версия пакета matplotlib.
© Импорт основного модуля построения диаграмм, представленного
аббревиатурой pit.

0 Выбор стиля seaborn (http : / / bit .ly / 2KaPFhs )
© Выбор гарнитуры serif для всех графиков.

216

Глава 7

Одномерные наборы данных
Основная функция построения диаграмм
передать два набора значений.



pit . plot ( ) . Ей необходимо

Координаты х
Список или массив, содержащий координаты х ( значения по оси абсцисс).

Координаты у
Список или массив, содержащий координаты у ( значения по оси ординат).

Разумеется, количество значений в обоих наборах должно совпадать. Рассмотрим следующий пример, результат выполнения которого представлен на
рис. 7.1.

1.0

0.5

0.0

0.5

1.0
0.0

2.5

5.0

7.5

10.0

12.5

15.0

17.5

Рис. 7.1. График для заданных значений хиу
In [ 7 ] : import numpy as np

In [ 8 ] : np . random . seed ( lOGO )

О

In [ 9 ] : у

_

=

np . random . standard normal ( 20 )

In [10 ] : x = np . arange ( len ( y ) )
pit . plot ( x , у ) ; О

©

©

Визуализация данных

217

О Фиксируем затравочное значение генератора псевдослучайных чисел,
чтобы пример можно было легко воспроизвести.

© Получение набора псевдослучайных чисел (значений у ).
© Получение диапазона целых чисел (значений х ).
© Вызов функции pit .plot ( ) и передача ей объектов х и у.

.

Функция pit plot ( ) автоматически распознает объекты ndarray, поэтому
в данном случае значения х избыточны. Если передаются только значения у ,
функция берет соответствующие индексы в качестве значений х Это означает, что следующая строка кода сгенерирует точно такой же график (рис 7.2)

.

.

.

In [ 11 ] : plt plot ( y ) ;

1.0

0.5

0.0

0.5

1.0

0.0

2.5

5.0

7.5

10.0

12.5

15.0

17.5

. .

Рис 7.2 График для заданного массива ndarray

(
iff

218

Массивы NumPy и пакет matplotlib
Функциям matplotlib можно передавать объекты ndarray библио теки NumPy, что позволяет существенно сократить код построения
| диаграмм. Но следите за тем, чтобы передаваемые массивы не были
слишком большими и/или сложными.

Глава 7

.

Поскольку большинство методов объекта ndarray в свою очередь возвращает массив ndarray, при выполнении функции pit plot ( ) можно вызвать
определенный метод (или даже цепочку методов). Например, при вызове метода cumsum( ) будет построен график изменения накопительной суммы (рис 7.3).

.

.

.

.

0.0

2.5

In [ 12 ] : plt plot ( y cumsum ( ) ) ;

0.5

0.0

-0.5

- 1.0

-1.5

- 2.0

5.0

7.5

10.0

12.5

15.0

17.5

. .

Рис 7.3 График для заданного массива ndarray с вызовом метода curnsum( )

Заданный по умолчанию стиль диаграммы далеко не всегда соответствует
требованиям публикации. Чаще всего приходится изменять шрифты (например, для обеспечения совместимости со шрифтами LaTeX), добавлять подписи к осям, отображать координатную сетку и т.п. Проще всего использовать

.

готовые стили диаграмм Кроме того, в пакете matplotlib имеется большое
число функций, предназначенных для настройки диаграмм Некоторые из них
просты и понятны, а некоторые требуют более детального изучения. Легче
всего разобраться с функциями, которые отвечают за настройку координат ной сетки и осей (рис. 7.4).

.
.
.

.

.

In [ 13 ] : plt plot ( y cumsum ( ) )

plt grid( False ) О
pit axis ( ' equal ' );

©

О

Скрывает координатную сетку.

0

Выравнивает масштаб обеих осей.
Визуализация данных

219

4
2
О

-2
-4
-6

0.0

2.5

5.0

7.5

10.0

12.5

15.0

17.5

Рис. 7.4. График без координатной сетки

.

Различные параметры функции plt axisQ перечислены в табл. 7.1. В ос новном это должны быть объекты str .

Таблица 7.1. Параметры функции p l t . a x i s ( )
Параметр

Описание

Возвращает текущие предельные значения осей
off

Скрывает линии осей и подписи к ним

equal

Выравнивает масштаб осей

scaled

Выравнивает масштаб за счет изменения размерностей

tight

Приводит к отображению всех данных ( за счет сжатия осей)

image

Приводит к отображению всех данных ( предельные значения определяются данными)

[ xmin, xmax ,
ymin, ymax ]

Устанавливает предельные значения на осях

Можно также непосредственно задать минимальные и максимальные значения каждой оси, воспользовавшись методами plt xlimQ и plt ylimQ
Рассмотрим пример (рис 7.5 ).

.

.

.
.
.

.

In [ 14 ] : plt plot ( y cumsum ( ) )
plt xlim( ~l, 20 )
plt ylim( np min( y cumsum ( ) )
1,
np max ( y cumsum ( ) ) + 1);

220

Глава 7

.
.

.

.

.

.

1

0

-1

-2

-3
0.0

2.5

5.0

7.5

10.0

12.5

15.0

17.5

20.0

Рис. 7.5. График с измененными пределами осей

Для наглядности график обычно снабжают всевозможными надписями,
например заголовком и подписями осей х и у. Они добавляются с помощью
функций plt . titleQ , plt . xlabelQ и pit . ylabel ( ) . По умолчанию функция plot ( ) строит непрерывный график даже для дискретных данных. Чтобы
получить график с маркерами, необходимо выбрать другой стиль. В следующем примере выводятся красные маркеры, а соединительные линии имеют
синий цвет и толщину 1,5 пункта ( рис. 7.6 ) .
In [ 15 ] : plt . figure ( figsize = ( 10 , 6 ) ) О
plt . plot ( y . cumsurnQ , ' b ' , lw= 1.5 )
plt . plot ( y . curnsum ( ) , ' го ' ) ©
pit . xlabel ( ' Индекс ' ) ©
pit . ylabel ( ' Значение ' ) ©
pit . title ( ' Простой график ' ) ; ©

©

О Увеличение размера рисунка.

©
©
©
©
©

Рисование графика синей линией толщиной 1,5 пункта.
Нанесение красных точек данных.

Добавление подписи к оси X.
Добавление подписи к оси Y.

Добавление заголовка графика.
Визуализация данных

221

Простой график
0.5

0.0

-0.5

Datetimelndex: 1547 entries, 2013 -01-01 22:00:00 to
2017 - 12- 31 22:00:00
Data columns (total 8 columns):
BidOpen
1547 non - null float64
BidHigh
1547 non - null float64
BidLow
1547 non - null float64
BidClose
1547 non - null float64
AskOpen
1547 non - null float64
AskHigh
1547 non - null float64
AskLow
1547 non - null float64
AskClose
1547 non - null float64

248

Глава 7

dtypes: float64(8)
memory usage: 108.8 KB

= raw[['AskOpen', ' AskHigh ' , ' AskLow', 'AskClose']]
quotes = quotes.Hoc[ 60 : ] ©
quotes.tailQ ©

In [56]: quotes

©

Out[56]:
2017 - 12 - 25
2017 - 12 - 26
2017 - 12 - 27
2017 - 12 - 28
2017 12 - 31

-

22: 00 : 00
22: 00 : 00
22: 00 : 00
22: 00 : 00
22: 00 : 00

AskOpen
1.18667
1.18587
1.18885
1.19426
1.20092

AskHigh
1.18791
1.19104
1.19592
1.20256
1.20144

AskLow AskClose
1.18467 1.18587
1.18552 1.18885
1.18885 1.19426
1.19369 1.20092
1.19994 1.20144

О Считывание финансовых данных из CSV-файла.

© Полученный объект Data Frame содержит много столбцов и более 1500
строк данных.

© Выбор 4 столбцов из объекта DataFrame (формат “ Open - High - Low-Close”
или OHLC) .

© Выборка подмножества данных объекта DataFrame.
0 Отображение последних пяти строк полученного набора данных.
В процессе инициализации объект QuantFig получает данные объекта
DataFrame и выполняет ряд базовых настроек. Визуализация данных, храня щихся в объекте QuantFig , осуществляется с помощью метода qf . iplotQ
( рис. 7.25) .

In [ 57 ] : qf

= cf.QuantFig(

quotes, ©
title= 1 Валютный курс пары EUR/USD ',
legend ='top', ©

name=' EUR/USD '

©

©

)

In [ 58 ] : plyo.iplot(
qf.iplot(asFigure=True),
image= ' png ' ,
filename= 'qf 01'

_

)

Визуализация данных

249

О Передача объекта DataFrane конструктору класса QuantFig.

© Добавление заголовка диаграммы.

© Размещение легенды в верхней части диаграммы.
© Название набора данных.
Валютный курс пары EUR/ USD
EUR/USD

т

1.2

ъ
1.19

I

1.18

(

1.17

И

Й

t

1.16

Nov 12

Oct 29
2017

Dec 10

NOV 26

Dec 24

Рис. 7.25. Диаграмма OHLC для валютной пары EUR/ USD

Объект QuantFig поддерживает различные методы, позволяющие добафинансовые обозначения, например линии
Боллинджера (рис. 7.26).
вить на диаграмму специальные

.

In [ 59 ] : qf add _bollinger _bands ( periods=15,
boll std= 2 )

_

.

.

In [ 68 ] : plyo iplot ( qf iplot ( asFigure=True ) ,
image= ' png ' ,
filenane= ' qf _ 02 '

250

Глава 7

©
©

О Количество периодов для линий Боллинджера.

© Количество среднеквадратических отклонений, применяемых при вычислении линий Боллинджера.

Валютный курс пары EUR/ USD
BOLL( AskClose,15)

Я

"

EUR/ USD

1.2

А
1.19

{

I

1.18

?

9

1.17

Ф

1.16

t

1.15
Nov 12

Oct 29
2017

Nov 26

Dec 10

Dec 24

Puc. 7.26. Добавление линий Боллинджера на диаграмму OHLC
для валютной пары EUR/ USD

Кроме того, на такие диаграммы можно добавлять вспомогательные инди каторы, например RSI ( Relative Strength Index индекс относительной силы),выводимые в отдельной области построения ( рис. 7.27 ).



_

In [ 61] : qf . add rsi ( periods =14 , О
showbands = False )
In [ 62 ] : plyo . iplot (
qf iplot ( asFigure = True ) ,
image = ' png ' ,
filename = ' qf 03 '
)

©

.

_

Визуализация данных

251

О Задание числа периодов для индикатора RSI.
© Подавление вывода нижнего и верхнего пределов.
Валютный курс пары EUR/ USD
BOLL(AskClose,15)

-

RSI ( AskClose,14) -И EUR/ USD

1.2

i
1.19

0

*

1.18

1.17

f

t
4

1.16

1.15
Oct 29

Nov 12

Nov 26

Dec 10

Dec 24

2017

80
60
40
20

Puc. 7.27. Добавление линий Боллинджера и индикатора RSI на диаграмму
OHLC для валютной пары EUR/ USD

Резюме
Пакет matplotlib считается стандартным средством визуализации данных
в Python. Он тесно интегрирован с пакетами NumPy и pandas, а его базовые
функции просты и удобны. В то же время программный интерфейс пакета
достаточно обширен и сложен, из-за чего нереально охватить все его возможности в рамках одной главы.
В этой главе были описаны основные функции пакета natplotlib, применяемые для создания двух- и трехмерных финансовых диаграмм. Дополни тельные примеры визуализации будут приведены в других главах.
Кроме того, в главе рассматривалась библиотека plotly, которая в связке с
пакетом Cufflinks позволяет легко создавать интерактивные диаграммы формата D3.js, поскольку для этого достаточно вызвать один -единственный метод
252

Глава 7

объекта DataFrame , а всеми деталями визуализации управляет сервер. Кроме
того, в пакете Cufflinks имеется также объект QuantFig , который упрощает
построение стандартных финансовых диаграмм, отображающих наиболее
важные финансовые индикаторы.

Дополнительные ресурсы
Дополнительная информация о пакете matplotlib доступна на следующих
сайтах:



домашняя страница официального сайта пакета, которая послужит хорошей отправной точкой ( https : / / natplotlib . org / index . html );

• галерея примеров ( https : / / matplotlib . org / gallery . html );
• руководство по созданию двухмерных диаграмм ( https : // matplotlib .
org / users / pyplot _ tutorial . html );
• руководство по созданию трехмерных диаграмм ( https : / / matplotlib .
org / mpl _ toolkits / mplot 3d / tutorial . html ) .



Галерея примеров самый удобный ресурс для выбора нужного способа
визуализации данных, поскольку для каждого типа диаграммы сразу же предлагается готовый код.
Информация о библиотеке plotly и пакете Cufflinks доступна на следующих сайтах:

• официальный сайт проекта plotly ( https : / / plot . ly );
• руководство по использованию библиотеки plotly в проектах Python
( https : / / plot . ly / python / getting - started );
• страница пакета Cufflinks на сайте GitHub ( https : / / github . com /
santosjorge / cufflinks ) .

Визуализация данных

253

ГЛАВА 8

Финансовые временные ряды
Время мешает всему происходить одновременно.
Рэй Каммингс

Временные ряды играют важную роль в финансовом анализе. Это наборы
данных, которые индексируются по дате и / или времени. В качестве примера
можно привести данные об изменении стоимости акций. Колебания курса
доллара к евро тоже представляют собой временной ряд. Текущий валютный
курс соответствует короткому промежутку времени , а совокупность таких котировок образует временной ряд.
Нет такой финансовой дисциплины, в которой не учитывался бы фактор
времени. В финансовом анализе оно играет не менее важную роль, чем в физике и других точных науках. В Python основный инструмент для работы с
временными рядами библиотека pandas. Уэс Маккинни, автор и основной
разработчик библиотеки, приступил к ее созданию, работая аналитиком в
AQR Capital Management, одном из крупнейших хедж -фондов. Таким образом,
можно смело утверждать, что библиотека pandas с самого начала предназна чалась для работы с финансовыми временными рядами.
В главе рассматриваются следующие темы.



Финансовые данные
В этом разделе вы познакомитесь с общими принципами обработки фи нансовых временных рядов с помощью библиотеки pandas и научитесь
выполнять основные операции, такие как импорт данных, получение
статистической сводки, вычисление изменений во времени и прорежи вание данных.
Скользящая статистика
В финансовом анализе скользящая статистика играет важную роль. Это
статистический показатель, вычисляемый за фиксированный промежуток времени с перемещением вперед по всему временному ряду. Наиболее



популярный пример скользящее среднее. Из этого раздела вы узнаете
о том, как вычислить такую статистику с помощью библиотеки pandas.

Корреляционный анализ
В этом разделе мы рассмотрим пример работы с временными рядами на
основе фондового индекса S&P 500 и индекса волатильности VIX. Мы
постараемся подтвердить тот ( эмпирический ) факт, что между двумя
индексами существует отрицательная корреляция.
Высокочастотные данные
Этот раздел посвящен высокочастотным, или тиковым, данным, которые широко применяются в финансовом анализе. В библиотеке pandas
имеются удобные средства для работы с такими данными.

Финансовые данные
В этом разделе мы будем работать с локально хранящимися финансовыми
данными, представленными в формате CSV. С технической точки зрения CSVфайл представляет собой простой текстовый файл с построчной структурой,
в которой отдельные значения разделяются запятыми. Прежде чем импорти ровать такой файл, необходимо подключить несколько пакетов и задать ряд

настроек.
In [1]: import numpy as пр
import pandas as pd
from pylab import mpl , pit
pit . style . use ( ' seaborn ' )
mpl . rcParams [ ' font . family ' ]
%matplotlib inline

= ' serif '

Импорт данных
Для импорта финансовых данных, сохраненных в наиболее распростра ненных форматах ( CSV, SQL, Excel и др.) , в библиотеке pandas предусмотрен
целый ряд функций и методов объекта Data Frame . В следующем примере с помощью функции pd . read _csv ( ) импортируется временной ряд, хранящийся
в CSV-файле1.
In [ 2 ] : filename

= ' . . / . . / source / tr _eikon _eod _data . csv ' О

open ( filename , ' г ' )
f . readlines ( ) [ : S ] ©

In [ 3] : f

1

=

©

В файле хранятся данные о стоимости различных финансовых инструментов на конец торгов
( EOD), предоставляемые службой Thomson Reuters Eikon Data.

256

Глава 8

Out[3]: ['Date,AAPL.O,MSFT.O,INTC.O,AMZN.O,GS.N ,SPY,.SPX,.VIX,EUR =,
XAU=,GDX,,GLD\n ' ,
12010 01 01,,,,,,,,,1.4323,1096.35,,\n',
' 2010 01 04,30.57282657,30.95,20.88,133.9,173.08,113.33,
1132.99 20.04 1.4411 1120.0 47.71 109.8\n ' ,
' 2010 01 05,30.625683660000004,30.96,20.87,134.69,176.14,
113.63 1136.52 19.35 1.4368 1118.65 48.17 109.7\n ' ,
' 2010 01 06,30.138541290000003,30.77,20.8,132.25,174.26,
113.71 1137.14 19.16 1.4412 1138.5 49.34 111.51\n']

- - . .. .
- . .. .
- . .. .

In [4]: data

=

_

. .
. . .
. . .

pd.read csv(filename, ©
index col=0, ©
parse dates=True)

_
_

©

In [5]: data.infoQ ©

Datetimelndex: 2216 entries, 2010 -01-01 to 2018 -06 - 29
Data columns (total 12 columns):
AAPL.0
2138 non - null float64
MSFT.0
2138 non - null float64
INTC.0
2138 non - null float64
2138 non - null float64
AMZN.0
GS.N
2138 non - null float64
SPY
2138 non - null float64
2138 non - null float64
.SPX
.VIX
2138 non - null float64
EUR=
2216 non - null float64
XAU=
2211 non - null float64
GDX
2138 non - null float64
GLD
2138 non - null float64
dtypes: float64(12)
memory usage: 225.1 KB

О Задание пути к файлу.

© Считывание первых пять строк необработанных данных ( Linux/ Mac).
© Передача имени файла в функцию pd.read csv().

_

© Выбор индексов из первого столбца.
@

Назначение индексам типа данных datetime.

© Полученный объект DataFrame.
Финансовые временные ряды

257

На этом этапе можно просмотреть имеющийся набор данных или построить базовые графики ( рис. 8.1).

In [б]: data.headQ
0ut[6]:

О
AAPL.O MSFT.O INTC.O AMZN.O

GS.N

SPY \

Date
2010-01-01
2010-01-04
2010-01-05
2010 01 06
2010-01-07

- -

NaN

NaN

NaN

NaN

30.572827
30.625684
30.138541
30.082827

30.950
30.960
30.770
30.452

20.88
20.87
20.80
20.60

133.90
134.69
132.25
130.00

.VIX

EUR =

NaN 1.4323
1.4411
1.4368
1.4412
1.4318

.SPX
Date
2010-01-01
2010 01-04
2010-01-05
2010-01-06

-

2010 01-07

NaN
1132.99
1136.52
1137.14
1141.69

20.04
19.35
19.16
19.06

In [7]: data.tailQ ©
Out[7]:
AAPL.O MSFT.O INTC.O
Date
2018-06- 25
2018-06 - 26
2018-06 - 27
2018-06 - 28
2018-06 - 29

Date
2018-06 -25
2018-06 - 26
2018 06-27
2018-06- 28
2018-06- 29

-

NaN

NaN
173.08
176.14
174.26
177.67

113.33
113.63
113.71
114.19

XAU =

GDX

GLD

1096.35
1120.00
1118.65
1138.50
1131.90

NaN

NaN

47.71
48.17
49.34
49.10

109.80
109.70
111.51
110.82

AMZN.O

GS.N

SPY

221.54
221.58
220.18
223.42
220.57

271.00
271.60
269.35
270.89
271.28

182.17
184.43
184.16
185.50
185.11

98.39
99.08
97.54
98.63
98.61

50.71
49.67
48.76
49.25
49.71

1663.15
1691.09
1660.51
1701.45
1699.80

.SPX

.VIX

EUR =

XAU =

GDX

GLD

2717.07
2723.06
2699.63
2716.31
2718.37

17.33
15.92
17.91
16.85
16.09

1.1702
1.1645
1.1552
1.1567
1.1683

1265.00
1258.64
1251.62
1247.88
1252.25

22.01
21.95
21.81
21.93
22.31

119.89
119.26
118.58
118.22
118.65

In [8]: data. plot(figsize=(18, 12), subplots=True);

©

О Отображение первых пяти...
© ...и последних пяти строк.
© Визуализация полученных наборов данных в разных областях построения.
258

Глава 8

200
AAPL.O

100
100

MS FT. О

50

50

INTC .O

25
1500

1000
500

AMZN.O

200

GS . N

100
200

SPY

100

2000

.SPX

1000

40
20
1.50

EUR =

1.25

1750
1500
1250

50

XAU =

WWV/ÿ0

GDX

25
GLD

150
100

T> >

-

Ф&

iP
Datetimelndex : 2216 entries, 2010 - 01- 01 to

In [ 11 ] : data



X

X

и
се

В

и

@

Й

х
а

Рис . 8.2. Средние значения процентных изменений

In Г 17 ]: data.pet changeQ .round(3).headQ ©
Out [17 ] :
AAPL.O MSFT.O INTC.O AMZN.O
Date
NaN
NaN
NaN
NaN
2010-01-01
NaN
NaN
NaN
NaN
2010-01 04
2010-01-05 0.002 0.000 - 0.000 0.006

-

2010-01-06
2010-01-07

264

Глава 8

- 0.016 - 0.006
- 0.002 - 0.010

- 0.003
- 0.010

GS.N

SPY \

NaN
NaN
NaN
NaN
0.018 0.003

- 0.018 - 0.011 0.001
- 0.017 0.020 0.004

а

^

.SPX

.VIX

EUR =

NaN
NaN
0.003

NaN
NaN
0.034
0.010
0.005

XAU=

GDX

GLD

NaN

NaN

0.006
0.003
0.003
0.007

0.022
0.001
0.018
0.006

NaN
NaN
0.010

NaN
NaN

Date
2010-01-01
2010-01-04
2010 0105
2010-01-06
2010-01-07

- -

0.001
0.004

-

-

-

-

-

0.024
0.005

-0.001
0.016
-0.006

_

In [18]: data.pct change().mean().plot(kind = ' bar ' ,
figsize=(10, 6));

О

©

_

Метод pct change() вычисляет процентное изменение между двумя ин дексированными значениями.

© Визуализация средних значений в виде столбчатой диаграммы.
Помимо простой ( процентной) доходности часто вычисляют также лога рифмическую доходность. В некоторых финансовых приложениях она оказы вается более информативной2. На рис. 8.3 показаны графики логарифмической
доходности по каждому из рядов. Такой способ визуализации предполагает
предварительную нормализацию данных.
In [19]: rets

= np.log(data

/ data.shlft(1))

©

In [20]: rets.headQ.round(3) ©
Out[20]:
AAPL.O MSFT.O INTC.O AMZN.O
Date
NaN
NaN
NaN
NaN
2010-01-01
NaN
NaN
NaN
NaN
2010-01-04
2010-01-05 0.002 0.000 0.000 0.006
2010-01-06 0.016 0.006 0.003 0.018
2010-01-07 0.002 0.010 0.010 0.017

-

2

-

-

.SPX

.VIX

Date
2010 01-01
2010 01-04
2010-01-05

-

NaN
NaN
0.003

2010-01-06
2010-01-07

NaN
NaN
-0.035

0.001
0.004

-0.010
-0.005 -

-

EUR =

GS.N

NaN
NaN
NaN
NaN
0.018 0.003
-0.011 0.001
0.019 0.004

XAU =

GDX

NaN

NaN

0.006
0.003
0.003
0.007

0.021
0.001
0.018
0.006

NaN
NaN
0.010

-

SPY \

-

GLD

NaN
NaN
0.001
0.024 0.016
0.005 - 0.006

-

Одним из преимуществ такого способа оценки является аддитивность во времени , не свой ственная простой ( процентной ) доходности.

Финансовые временные ряды

265

I n [ 21] : rets

.cumsumQ . apply ( np.exp ) . plot ( figsize=( 10,

©

6 ) );

О Вычисление логарифмической доходности векторизованным способом.

© Получение выборки.
© Построение графиков накапливаемой логарифмической доходности: сначала вызывается метод cumsum ( ), а затем к полученным результатам при-

.

.

меняется метод np expQ

ю

8

AAPL.O
MSFT.O
INTC .O
AMZN. O
GS. N
SPY
.SPX
.VIX

EUR =
6

XAU =
GDX
GLD

4

2

0

2010

2011

2012

2013

2014

2015

2016

2018

2017

Date

. .

Рис 8.3 Накапливаемая логарифмическая доходность

Прореживание данных
Прореживание — важная операция в анализе финансовых временных
рядов. Обычно происходит децимация, или уменьшение частоты выборки,
когда, например, тиковые данные агрегируются в минутные интервалы либо
временной ряд, содержащий суточные значения, обобщается в недельные или
месячные наблюдения (рис. 8.4).

.

.

I n [ 22 ] : data resample ( ' lw ' , label= ' right ' ) lastQ
Out [ 22 ] :
AAPL . O MSFT . O INTC . O AMZN O

.

Date
2010 01-03
NaN
2010-01-10 30.282827

-

266

Глава 8

NaN
30.66

NaN
20.83

NaN
133.52

.headQ
.

GS N

©
SPY

NaN
NaN
174.31 114.57

\

.
.

2010-01- 17 29.418542
2010-01- 24 28.249972
2010 01 31 27.437544

30 86
28 . 96
28 18

- -

.SPX .VIX
Date
2010 01-03
2010-01-10
2010-01- 17
2010-01- 24
2010-01-31

-

In [ 23 ] : data
0 ut [ 23 ] :

.

20.80 127.14 165 21 113.64
19 . 91 121.43 154.12 109.21
19 . 40 125.41 148 . 72 107.39

.

EUR =

XAU =

GDX

GLD

NaN
NaN 1.4323
1144.98 18.13 1.4412
1136.03 17.91 1.4382
1091.76 27.31 1.4137
1073.87 24.62 1.3862

1096.35
1136.10
1129.90
1092.60
1081.05

NaN
49.84
47.42
43.79
40.72

NaN
111.37
110.86
107.17
105.96

. i-esample ( ' lm

.

label = ' right ' ) . last ( ) headQ

AAPL.O

MSFT.O INTC.O

e

AMZN.O

GS.N

SPY

125.41
118.40
135.77
137.10
125.46

148.72
156.35
170.63
145.20
144.26

107.3900
110.7400
117.0000
118.8125
109.3690

Date
2010-01- 31
2010 02- 28
2010-03 - 31
2010 04-30
2010-05 -31

-

27.437544
29.231399
33.571395
37.298534
36.697106

.SPX

28.1800
28.6700
29.2875
30.5350
25.8000

.VIX

19.40
20.53
22.29
22.84
21.42

EUR =

XAU=

GDX

GLD

Date

-

2010-01 31
2010-02- 28
2010-03-31
2010 04-30
2010 05 - 31

-

1073.87
1104.49
1169.43
1186.69
1089.41

24.62 1.3862
19.50 1.3625
17.59 1.3510
22.05 1.3295
32.07 1.2305

1081.05 40.72 105.960
1116.10 43.89 109.430
1112.80 44.41 108.950
1178.25 50.51 115.360
1215.71 49.86 118.881

In [ 24 ] : rets . cumsumQ . apply ( np . exp ) . resample ( ' lm ' ,
label ' right ' ) lastQ plot ( figsize= ( 10 , 6 ) ) ;

=

.

.

©

О Данные EOD (суточная выборка ) агрегируются в недельные ...
Q

... и месячные интервалы .

Q

Построение графиков накапливаемой логарифмической доходности:
сначала вызывается метод cumsumQ , затем к полученным результатам
применяется метод пр . ехр ( ) , после чего выполняется месячное проре живание.

Финансовые времени ь/е ряды

267

10

8

6

AAPL . O
MS FT . О
INTC . O
AM /.N . O
GS . N
SPY
.SPX
VIX
HUH
XAU
GDX
GLD

4

2

0
2010

2011

2012

2013

2014

2015

2016

2017

2018

Date

Puc. 8.4. Накапливаемая логарифмическая доходность
( месячные интервалы )

(

Избегайте смещенных прогнозов
Во многих операциях прореживания библиотека pandas по умол чанию использует метку ' l e f t ' ( выбор левого индекса ). Но для
|получения корректных финансовых прогнозов следует использовать метку ' r i g h t ' ( выбор правого индекса ), т.е. самую последнюю
доступную точку данных в интервале. В противном случае финан 3
совый прогноз может оказаться смещенным

Скользящая статистика
В финансовом анализе часто применяются скользящие статистики, назы ваемые индикаторами. Они служат базовыми инструментами технического
анализа. В этом разделе мы будем работать только с одним временным рядом
из нашего набора.

3

Смещенный прогноз возникает, когда в ходе финансового анализа используются данные, получаемые только в будущих периодах. Результат, как правило, оказывается “ чересчур оптимистичным”. Это может проявляться , например, при тестировании торговой стратегии на
исторических данных.

268

Глава 8

In [ 25 ] : sym

' AAPL . O '

=

.

pd DataFrame ( data [ sym ] ) . dropnaQ

=

In [ 26 ] : data

.

In [ 27 ] : data tailQ
0 ut [ 27 ] :

.

AAPL O
Date

2018
2018
2018
2018
2018

- 06 - 25
- 0606 -- 2726
-06 - 28

-

182.17
184.43
184.16
185.50
06 - 29 185.11

Общие сведения
С помощью библиотеки pandas можно легко вычислить стандартные
скользящие статистики.
In [ 28 ] : window

=

О

20

In [ 29 ] : data [ ' min ' ]
In [ 39 ] : data [ ' mean ' ]

In [ 31] :

.

data [ sym ] . rolling ( window = window ) min ( )

=
=

.

©

.

©

data [ sym ] rolling ( window = window ) mean ( )

datafstd ' ] = data [ sym ] . rolling ( window =window ) . std ( ) ©

In [ 32 ] : data [ ' median ' ]

=

data [ sym ] . rolling ( window = window ) . medianQ

.

data [ sym ] . rolling ( window= window ) max ( )

In [ 33 ] :

datafmax ' ] =

In [ 34 ] :

datafewma ' ] = data [ sym ] .ewm ( halflife=0.5, \

_

©

©

.

min periods = window ) meanQ

©

О Определение временного окна, т.е. числа охватываемых индексных значений.

О Вычисление скользящего минимального значения.
© Вычисление скользящего среднего.

© Вычисление скользящего среднеквадратического отклонения.

© Вычисление скользящей медианы.
Финансовые временные ряды

269

© Вычисление скользящего максимального значения.
О Вычисление экспоненциально взвешенного скользящего среднего с полупериодом затухания 0,5.

Для вычисления других технических индикаторов следует воспользоваться
другими библиотеками (в частности, пакетом Cufflinks, описанным в главе 7).
Можно также применять пользовательские индикаторы с помощью метода
applyQ .
В следующем примере выводится фрагмент имеющегося набора и визуализируется несколько скользящих статистик (рис. 8.5).
min
190

mean
max

~'

/'

N

f

r

180
!
у

\

\

170

у
»

Г

/

160

v
|

у

150

^

а®4

тРч

а®4

.JCP

O

г»4

ай51

г»4

оЛ6

т«ч

а®4

а«ч

Date

. .

Рис 8.5 Скользящая статистика для минимального, среднего
и максимального значений

.

.

I n [ 35 ] : data dropnaQ head( )
0ut [ 35 ] :
AAPL. O
min
Date
2010- 02- 01 27.818544 27.437544
2010- 02- 02 27.979972 27.437544
2010- 02- 03 28.461400 27.437544

2010 - 02- 04
2010- 02- 05

270

Глава 8

27.435687
27.922829

27.435687
27.435687

mean

std

29.580892
29.451249
29.343035
29.207892
29.099892

0.933650
0.968048
0.950665
1.021129
1.037811

.tf

o

median
Date
2010 02 - 01
2010
2010
2010
2010

-02 - 02

-02 - 03
-0202-- 0405
-

max

29.821542 30.719969
29.711113 30.719969
29.685970 30.719969
29.547113 30.719969
29.419256 30.719969

ewma
27.805432
27.936337
28.330134
27.659299
27.856947

data [ [ ' min ' , ' mean ' , ' max ' ]] . iloc [ - 208 : ] . plot (
figsize = ( lG , 6 ) , style = [ ' g - - ' , 1 r - ' , ' g - - ' ] , lw 0.8 )
data [ sym ] . iloc [ - 2 O0 : ] plot ( ax = ax , lw= 2.8 ) ; ©

In [ 36 ] : ax

=

-

.

=

О

О Построение графиков трех скользящих статистик для последних 200
строк набора.

© Добавление исходного временного ряда на диаграмму.

Пример технического анализа



Скользящие статистические показатели основной инструмент технического анализа, противопоставленного фундаментальному анализу, который в
первую очередь опирается на данные финансовых отчетов и производствен ные показатели компании.
Традиционная торговая стратегия, основанная на техническом анализе,
предполагает вычисление двух простых скользящих средних (Simple Moving
Average SMA ) . Идея заключается в том, что трейдер покупает акции ( или
другой финансовый инструмент ), когда график краткосрочного SMA пересекает график долгосрочного SMA снизу вверх, и продает их, когда имеет место
противоположная ситуация ( пересечение сверху вниз ). Реализовать такую
стратегию можно с помощью объекта DataFrame библиотеки pandas.
Расчет скользящей статистики становится возможным только для наборов данных, размер которых больше временного окна ( значения параметра
window ). На рис. 8.6 можно увидеть, что вычисление скользящего среднего начинается только тогда, когда набирается достаточное количество наблюдений
с учетом параметров конкретного примера.



.

In [ 37 ] : data [ ' SMA1 ' ]

=

data [ sym ] rolling ( window = 42 ) . meanQ

In [ 38] : data [ ' SMA 2 ' ]

=

data [ sym ] . rolling ( window= 252 ) meanQ

.

©
©

.

In [ 39 ] : data [ [ sym , ' SMA1 ' , ' SMA 2 ' ]] tail ( )

Финансовые времени we ряды

271

Out[39]:

AAPL.O

SMA1

SMA2

182.17
184.43
184.16
185.50
185.11

185.606190
186.087381
186.607381
187.089286
187.470476

168.265556
168.418770
168.579206
168.736627
168.901032

Date

-

2018-06 25
2018-06 26
2018-06 27
2018-06 28
2018-06 - 29

In [40]: data[[sym, ' SMA 1 ' , ' SMA 2 ' ]] . plot(figsize=(10, 6));

©

О Вычисление значений краткосрочного SMA.

© Вычисление значений долгосрочного SMA.
© Визуализация котировок и временных рядов SMA.
200

AAPL. O
SMA1
SMA 2

175

150

125

100

75

50

25

тРЛ~

А4

Date

Рис . 8.6. Котировки акций компании Apple и два простых
скользящих средних



В рассматриваемом контексте SMA
просто удобный визуальный ин ,
обозначить
позиции для реализации торговой
дикатор задача которого
стратегии. На рис. 8.7 длинной позиции соответствует значение 1, а короткой
позиции значение -1. Изменение позиции обозначается ( визуально ) пересечением графиков двух SMA.





272

Глава 8

In [ 41] : data . dropna ( inplace =True )
In [ 42 ] : data [ ' Позиции ' ]

In [ 43 ] : ax

=

О

np . where ( data [ ' SMA1 ' ] > data [ ' SMA 2 ' ] ,
1, ©
~ i) ©

©

= data [ [ sym ,

' SMA 1 ' , ' SMA 2 ' ,
' Позиции ' ]] . plot ( figsize = ( 10 , 6 ) ,
secondary y = ' Позиции ' , nark right = False )
ax . getjlegend ( ) . set _ bbox _ to_anchor ( ( 0 , 25 , 0.85 ) ) ;

_

_

О Учитываются только полные строки данных.

© Если краткосрочное SMA больше долгосрочного SMA...
© ...то нужно открывать длинную позицию (уровень 1).
© В противном случае следует открывать короткую позицию (уровень -1).
200

1.00

180

0.75

AAPL . 0
SMA1
SMA2
Позиции

160
140

100

АЛ
J/ V
0

.
ВО

40

п
J / "to
,

120

'

"

]

FX
JX

Jf /

0.50

0.25
0.00

-0.25

-0.50
-0.75

/

-1.00

Date

Рис. 8.7. Котировки акций компании Apple, два простых
скользящих средних и позиции

Неявная торговая стратегия приведет в данном случае к заключению всего
нескольких сделок, которые будут происходить при изменении позиции ( т.е. в
точках пересечения графиков SMA ). Учитывая сделки при открытии и закры тии торгов, суммарно будет всего шесть биржевых операций.

Финансовые временные ряды

273

Корреляционный анализ
Остальные возможности библиотеки pandas по обработке финансовых
временных рядов мы будет рассматривать на примере фондового индекса
S&P 500 и индекса волатильности VIX. Известно, что, когда индекс S&P 500
растет, индекс VIX обычно падает, и наоборот. Такое поведение индексов обусловлено корреляцией, а не причинно-следственными связями. В этом разделе
мы постараемся найти статистическое подтверждение сильной отрицательной корреляции между индексами S &P 500 и VIX4.

Исходные данные
Набор анализируемых данных теперь представлен двумя временными ря дами, графики которых показаны на рис. 8.8.

- .SPX
2500

2000
1500
1000
50

.VIX
40

30
20
10

г°ч

oV

&

&

Date

.

Рис. 8.10 Изменение логарифмической доходности индексов S& P 500 и VIX
276

Глава 8

Для визуализации подобных взаимосвязей в библиотеке pandas имеется функция scatter _matrix ( ) . С ее помощью можно построить диаграмму
зависимости двух логарифмических доходностей, снабдив ее гистограммой
столбцов данных либо диаграммой ядерной оценки плотности ( Kernel Density
Estimator KDE ), как показано на рис. 8.11.



_

In [ 53 ] : pd . plotting . scatter matrix ( rets , О
alpha =0.2 , ©
diagonal^ hist ' , ©
hist _ kwds ={ ' bins 1 : 35 } ,
figsize = ( 15 , 6 ) ) ;

0

О Визуализируемые данные.
Q

Параметр alpha , определяющий прозрачность точек.

@

Вспомогательная диаграмма, выводимая по диагонали; в данном случае
это гистограмма столбцов данных.

0 Аргументы, передаваемые функции, которая отвечает за построение
гистограммы.

0.039999999999999994
0.020000000000000004
0.0
-0.020000000000000004
-0.04

0.06

0.6
0.4

|0.2
0.0

TV

-0.2

S
.SPX

§

VIX

Рис. 8.11. Диаграмма рассеяния логарифмической доходности
индексов S & P 500 и VIX

Регрессионный анализ по методу наименьших квадратов
Теперь можно легко выполнить регрессионный анализ по методу наименьших квадратов ( МНК ). На рис. 8.12 показана диаграмма рассеяния логарифмической доходности двух индексов, на которой через облако точек проходит

Финансовые временнь/е ряды

277

линия регрессии. Отрицательный наклон регрессионной прямой указывает
на существование отрицательной корреляции между индексами.

.

.

.

I n [ 54 ] : reg = np polyfit ( r e t s [ ' SPX ' ] , rets [ ' VIX 1 ] , deg=l)

.

.

.

I n [ 55 ] : ax = rets plot ( kind= ' scatter ' , x= ' SPX ' , y= ' VIX ' ,
figsize=( 10, 6 ) ) ©
ax plot ( rets [ ' SPX ' ] , np polyval( reg, rets [ ' SPX ' ] ) ,
lw= 2 ) ; ©

.

.

О

.

.

'г ',

О Реализация линейной регрессии по методу наименьших квадратов.

© Построение диаграммы рассеяния логарифмической доходности...
©

...на которую накладывается линия регрессии.
0.8

0.6

0.4

X

>

0.2

••

0.0

••
• %,

-0.2

4

•|%

J. •

•• •

••

-0.4
-0.06

-0.04

-0.02

0.00

0.02

0.04

.SPX

.

.

Рис 8.12 Диаграмма рассеяния логарифмической доходности индексов
S& P 500 и VIX с линией регрессии

Корреляция
Наконец, можно переходить к непосредственной оценке корреляции. Мы
определим две метрики: статическую, рассчитанную для всего набора, и сколь зящую, вычисляемую в пределах временного окна. Как показано на рис. 8.13,
корреляция действительно изменяется во времени, но при этом, учитывая
параметризацию модели, она всегда отрицательная. Это служит наглядным
278

Глава 8

подтверждением того эмпирического факта, что между индексами S&P 500 и
VIX существует (сильная) отрицательная корреляция.
I n [ 56 ] : rets
Out [ 56 ] :
SPX

.
. VIX

.corrQ

О

. SPX

. VIX

1.000000
- 0.804382

- 0.804382

1.000000

.

.

.

In [ 57 ] : ax = rets [ ' SPX ' ] rolling ( window= 252 ) corr (
rets [ ' VIX ' ] ) plot ( figsize=( 10, 6 ) )
ax axhline ( rets corr ( ) iloc [ 0, 1] , c= ' r ' ) ; ©

.

.

.

.

.

©

О Корреляционная матрица для всего объекта DataFrame.
0 График скользящей корреляции.
@

Добавление статического значения в виде горизонтальной линии.

-0.725

-0.750

-0.775
-0.800

-0.825

-0.850

-0.875
-0.900

Ф

Ф&

хь

Ук

л*

Date

Рис. 8.13. Корреляция индексов S&P 500 и VIX ( скользящая и статическая )

Высокочастотные данные
В этой главе мы работаем с финансовыми временными рядами с помощью
инструментов библиотеки pandas. Тиковые данные представляют собой частный случай таких рядов. Их можно трактовать примерно так же, как и данные
EOD (котировки на конец торгов), с которыми мы имели дело в предыдущих
Финансовые временнь/е ряды

279

примерах. Импорт тиковых данных с помощью библиотеки pandas выполняется достаточно быстро. Вот как эта задача реализуется для набора из не скольких сот тысяч строк (рис. 8.14).
In [ 59 ] : mine
# Источник данных : FXCM Forex Capital Markets L t d
tick = pd read_csv ( ' / / source / fxcm_eur _usd_ tick _data csv ' ,
index _col=0, parse dates=True )
CPU tines : user 1.07 s, sys : 149 ns, total: 1.22 s
Wall tine : 1.16 s

In [ 60 ] : tick

.infoQ



Datetinelndex : 461357 entries, 2018 - 06 - 29 00 : 00: 00.082000 to
2018 - 06 - 29 20 : 59:00.607000
Data colunns ( total 2 colunns ) :
461357 non - null float 64
Bid
461357 non - null float 64
Ask
dtypes : float 64( 2 )
nenory usage: 10.6 MB

.

In [ 61] : tick [ ' Mid ' ] = tick nean( axis=l)

О

.

In [ 62 ] : tick[ ' Mid ' ] plot ( figsize=( 10, 6 ) ) ;

О Вычисление средней (Mid) цены для каждой строки.
1.168

1.166

1.164

1.162

1.160

1.158

1.156
фл?

фл

фл?

?

.

.

Ф

фл?

Ф

фл?

фл

?

фл

?

Рис 8.14 Тиковые данные валютного курса EUR / USD

280

Глава 8

фл?

При работе с тиковыми данными часто приходится выполнять прорежива ние временного ряда. В следующем примере набор агрегируется в пятиминутные интервалы ( рис. 8.15 ) , что позволяет тестировать торговые стратегии на
исторических данных или выполнять технический анализ.

_

=

In [ 63 ] : tick resam

_

.

.

tick resample( rule = ' 5min ' , label = ' right ' ) last ( )

.

In [ 64 ] : tick resam head ( )
0 ut [ 64 ] :

-

-

2018 06 29
2018 06 29
2018 06 - 29
2018-06 29
2018 06 29

_

00:05:00
00:10:00
00:15:00
00:20:00
00:25:00

Bid

Ask

Mid

1.15649
1.15671
1.15725
1.15720
1.15711

1.15651
1.15672
1.15727
1.15722
1.15712

1.156500
1.156715
1.157260
1.157210
1.157115

.

In [ 65 ] : tick resam [ ' Mid ' ] plot ( figsize = ( 18 , 6 ) ) ;

1.168

1.166

1.164

1.162

1.160

1.158

Гw

1.156

00: 00
29 -Jun

03:00

06 : 00

09:00

12: 00

15:00

18:00

21: 00

:

Puc. 8.15. Данные валютного курса EUR/ USD с пятиминутной агрегацией

Финансовые временные ряды

281

Резюме



В этой главе были рассмотрены принципы обработки временных рядов
наиболее важного типа данных в финансовых вычислениях. Библиотека
pandas содержит все необходимые средства не только для анализа таких дан ных, но и для их визуализации. Кроме того, библиотека позволяет считывать
финансовые временные ряды из самых разных источников, а также экспорти ровать их в различные форматы, о чем будет рассказано в следующей главе.

Дополнительные ресурсы
Хорошими источниками информации по теме данной главы послужат следующие книги.

• McKinney, Wes. Python for Data Analysis ( 2017, O’ Reilly).



282

VanderPlas, Jake. Python Data Science Handbook ( 2016, O’ Reilly).

Глава 8

ГЛАВА 9

Операции ввода-вывода
Создавать же версию, не имея фактов,



большая ошибка.
Шерлок Холмс





не только финансовых
Как правило, большая часть данных
хранится на жестких дисках ( HDD) или запоминающих устройствах другого типа.
Емкость устройств хранения все время растет, а их удельная стоимость ( в пересчете на мегабайт данных) непрерывно снижается .
Фактически емкость хранилищ растет более высокими темпами, чем объемы оперативной памяти, доступной даже в самых мощных компьютерах. Поэтому возникает потребность не только помещать данные на диск для постоян ного хранения, но и компенсировать нехватку оперативной памяти, создавая
для нее файл подкачки на диске.
С учетом вышесказанного в финансовых и других приложениях, работа ющих с большими объемами данных, операции ввода - ввода играют важную
роль. Зачастую именно они становятся узким местом, когда речь заходит о
производительных вычислениях, поскольку скорость дискового обмена сильно ограничена 1. Зачастую центральный процессор вынужден “ простаивать”
во время медленных операций ввода - вывода .
Несмотря на то что в большинстве финансовых и корпоративных систем
приходится иметь дело с большими данными ( где емкость уже измеряется петабайтами), отдельные аналитические задачи решаются на выборках среднего
размера. Вот цитата из исследования, проведенного компанией Microsoft.

Собранные нами и другими исследователями данные свидетельствуют о том,
что в большинстве реальных аналитических задач обрабатывается не более
100 Гбайт входных данных. В то же время такие популярные инфраструктуры ,
как Hadoop/ MapReduce, изначально проектировались в расчете на петабайтовые объемы данных.
Аппусвами (2013 )
1

В данном случае не проводится никаких различий между разными уровнями оперативной памяти и кеша процессора. Оптимальное использование оперативной памяти это отдельная тема.



Чаще всего в задачах финансового анализа обрабатывается максимум несколько гигабайтов данных
это именно тот объем, на который ориенти рованы встроенные инструменты Python и библиотеки научного стека, такие
как NumPy, pandas и PyTables. Наборы данных такого размера можно обрабатывать в оперативной памяти, что с учетом вычислительных мощностей
современных процессоров приводит к существенному повышению производительности. Но данные еще нужно прочитать с диска, а по окончании обра ботки результаты тоже записываются на диск, и это следует учитывать при



рассмотрении вопросов производительности.
В главе рассматриваются следующие темы.
Базовые операции ввода- вывода в Python
В Python имеются встроенные функции сериализации и записи объек тов на диск, а также считывания данных с диска в оперативную память.
Помимо этого, Python располагает средствами работы с текстовыми
файлами и реляционными базами данных. Библиотека NumPy также содержит специализированные функции для быстрого сохранения бинарных данных и чтения объектов ndarray.

Ввод и вывод данных с помощью библиотеки pandas
Библиотека pandas содержит множество удобных функций и методов,
предназначенных для чтения и записи данных в различных файловых
форматах ( включая CSV и JSON ).
Ввод и вывод данных с помощью PyTables
PyTables это пакет для управления иерархическими базами данных,
основанный на стандарте HDF5 и ориентированный на выполнение бы стрых операций ввода- вывода с огромными наборами данных. Скорость
в данном случае ограничена зачастую только техническими возможностями используемого оборудования.



Ввод и вывод данных с помощью TsTables
TsTables это надстройка к пакету PyTables, ускоряющая чтение и за пись временных рядов.



Базовые операции ввода-вывода в Python
В Python имеется множество средств ввода- вывода, часть из которых оп тимизирована с учетом производительности, а часть с учетом гибкости использования. Но в целом со всеми этими инструментами удобно работать как
в интерактивном режиме, так и в производственной среде.



284

Глава 9

Запись объектов на диск
Иногда объекты Python требуется записывать на диск, например для использования в других проектах, передачи другим разработчикам или напи сания документации к имеющемуся коду. Для решения такого рода задач
предназначен модуль pickle, позволяющий сериализовать большинство
объектов Python . Сериализация означает преобразование объекта из иерархической структуры в байтовый поток. Обратная операция называется десе риализацией.
Как всегда, сначала нужно импортировать рабочие модули и задать ряд ба зовых настроек ( для последующего построения диаграмм ).
In [ 1 ] : from pylab import pit , mpl
pit . style . use ( ' seaborn ' )

mpl . rcParams [ ' font . family ' ]
%matplotlib inline

=

' serif '

В следующем примере мы будем работать с псевдослучайными данными,
которые хранятся в объекте list ( список ).
In [ 2 ] : import pickle О
import numpy as np
from random import gauss
In [ 3 ] : a

= [ gauss ( 1.5 ,

In [ 4 ] : path

©

2 ) for i in range ( 1000080 ) ]

©

= / Users / yves / Temp / data / ' ©
1

In [ 5 ] : pkl_f i l e

= open ( path

+ ' data . pkl ' , ' wb ' )

©

О Импорт модуля pickle из стандартной библиотеки.

0 Импорт модуля gauss для генерирования случайных чисел с нормальным распределением.

0 Создание большого списка, заполняемого случайными числами.
О Путь для хранения файлов данных2.
0 Открытие файла для записи в двоичном режиме.

2

Можете, например, создать подкаталог temp в текущем каталоге и указать путь ' . / temp / ' . —
Примеч. ред.

Операции ввода - вывода

285

За сериализацию и десериализацию объектов Python отвечают функции
pickle.dumpQ и pickle.load().

_

In [6]: %time pickle.dunp(a, pkl file) О
CPU tines: user 37.2 ns, sys: 15.3 ns, total: 52.5 ns
Wall tine: 50.8 ns

_

In [7]: pkl file.close()

©

In [8]: ll $path* ©
- rw - г -- г - - 1 yves staff 9002006 Oct 19 12:11
/Users/yves/Tenp/data/data.pkl

_

In [9]: pkl file

= open(path + ' data.pkl', 'rb')

©

_

In [10]: %tine b = pickle.load(pkl file) ©
CPU tines: user 34.1 ns, sys: 16.7 ns, total: 50.8 ns
Wall tine: 48.7 ns
In [11]: a[:3]
Out[li]: [6.517874180585469, 0.5552400459507827, 2.8488946310833096]

-

In [12]: b[:3]
0ut[12]: [6.517874180585469, 0.5552400459507827, 2.8488946310833096]

-

In [13]: np.allclose(np.array(a), np.array(b))
Out[13]: True

О

©

Сериализация и сохранение объекта а в файл.

© Закрытие файла.
© Проверяем наличие файла на диске и его размер ( Mac/ Linux ).
О Открытие файла для чтения в двоичном режиме ( rb ).

© Чтение объекта с диска и его десериализация.

© Преобразование объектов а и Ь в объекты ndarray. Функция
np.allcloseQ позволяет убедиться в том, что оба объекта хранят одинаковые данные ( числа ).

Как видите, сохранить и загрузить отдельный объект с помощью модуля
pickle совсем не сложно. А как насчет сразу двух объектов?

286

Глава 9

_

In [ 14 ] : pkl file

=

open(path + ' data.pkl ' , ' wb ')

In [ 15 ] : %tine pickle.dunp(np.array(a), pkl_file) О
CPU tines: user 58.1 ns, sys: 6.09 ns, total: 64.2 ns
Wall tine: 32.5 ns

_

In [ 16 ] : %tine pickle.dunp(np.array(a) ** 2 , pkl file) ©
CPU tines: user 66.7 ns, sys: 7.22 ns, total: 73.9 ns
Wall tine: 39.3 ns

_

In [ 17 ] : pkl file.close()
In [ 18 ] : ll $path* ©
rw г г 1 yves staff 16000322 Oct 19 12:11
/Users/yves/Tenp/data/data.pkl

- - --

--

О Сериализация и сохранение ndarray - версии объекта а .
© Сериализация и сохранение ndarray - версии объекта а с возведением в
квадрат.

@

Размер файла увеличился почти вдвое.

Теперь рассмотрим, как обратно загрузить два объекта ndarray .
In [ 19 ] : pkl file = open(path + 'data.pkl 1 , ' rb ')

_

_

pickle.load(pkl file) ©
x[ : 4 ]
0ut[20]: array([ 6.51787418, 0.55524005,

In [ 20 ] : x

=

_

-

pickle.load(pkl file) ©
У[ :4]
Out[ 21 ]: array([42.48268383, 0.30829151,

In [ 21 ] : у

=

2.84889463,

5.94489175 ])

8.11620062, 35.34173791])

_

In [ 22 ] : pkl file.close()

О Получение объекта, сохраненного первым .

0 Получение объекта, сохраненного вторым .
Модуль pickle сохраняет объекты по принципу FIFO ( First In , First Out —
первым пришел , первым ушел) . Проблема здесь только одна: отсутствие метаинформации, с помощью которой пользователь мог бы заранее узнать, что
хранится в сериализованном файле.
Операции ввода-вывода

287

Одно из решений заключается в том, чтобы записывать в файл не одиноч ные объекты, а словарь, включающий все объекты.
In [ 23 ] : pkl _file = open ( path + ' data . pkl ' , ' wb ' )
pickle . dump ( { x ' : x , ' у ' : у } , pkl _file )
pkl _file . close ( )
1

О

In [ 24 ] : pkl _ file = open ( path + ' data . pkl ' , ' rb ' )
data = pickle . load ( pkl _file ) ©
pkl _file . close ( )
for key in data . keysQ :
print ( key , data [ key ][ : 4 ] )
x [ 6.51787418 - 0.55524005 2.84889463 5.94489175 ]
у [ 42.48268383 0.30829151 8.11620062 35.34173791 ]
In [ 25 ] : ! rm - f $ path*

О Сохранение словаря, содержащего два объекта ndarray .

0 Считывание словаря.
В этом способе все объекты записываются и считываются одновременно,
что в большинстве случаев можно считать разумным компромиссом.

Уу 2% г

%

Проблемы совместимости
Сериализовывать объекты с помощью модуля pickle достаточно
просто. Следует опасаться только одного: при обновлении пакета
новая версия может не распознать сериализованные объекты, созданные предыдущей версией. Трудности могут возникнуть и при
обмене объектами между разными операционными системами и
платформами. Именно поэтому предпочтительнее использовать
встроенные средства ввода - вывода таких библиотек, как NumPy и
pandas , о чем мы поговорим в следующих разделах.

Чтение и запись текстовых файлов
Python располагает богатым инструментарием для работы с текстовыми
данными, что находит активное применение в корпоративных и научных при ложениях. В Python можно работать как со строковыми объектами, так и с
текстовыми файлами в целом.
Предположим, необходимо передать большой объем данных в виде CSVфайла. Несмотря на то что такие файлы имеют внутреннюю структуру, по сути
это простые текстовые файлы. В следующем примере создаются два объекта:
288

Глава 9

ndarray, содержащий набор случайных чисел, и Datetimelndex, содержащий
почасовые данные. Затем они объединяются и сохраняются в CSV- файле.

In [26]: import pandas as pd

In [27]: rows = SG0G О
a = np.random.standard _ normal((rows, 5)).round(4)

©

In [28]: a ©
Out[28]: array([[ - 0.0892, - 1.0508, - 0.5942, 0.3367, 1.508 ],
[ 2.1046, 3.2623, 0.704 , - 0.2651, 0.4461],
[ - 0.0482, - 0.9221, 0.1332, 0.1192, 0.7782],

[ 0.3026,
[ - 0.7031,
[ 2.4573,

- 0.2005, - 0.9947, 1.0203, - 0.6578],
- 0.6989, - 0.8031, - 0.4271, 1.9963],
2.2151, 0.158 , - 0.7039, - 1.0337]])




In [29]: t = pd.date _ range(start ='2019/ l / l ', periods= rows,
freq = ' H ‘ )I ©
In [30]: t ©
Out[30]: Datetimelndex([' 2019 - 01 - 01
2019 - 01 - 01
i
2019 - 01 - 01
i
2019 - 01 - 01
i
2019 - 01 - 01
i

00:00:00 ',
02:00:00 ',
04:00:00 ',
06:00:00 ',
08:00:00 ',

' 2019 - 01 - 01
'2019 - 01 - 01
' 2019 - 01 - 01
'2019 - 01 - 01
' 2019 -01 - 01

01:00:00' >
03:00:00' »
05:00:00' »
07:00:00'
09:00:00' >



2019 - 07 - 27 22:00:00'' ,
2019 - 07 - 28 00:00:001',
i
2019 - 07 - 28 02:00:00 '1 ,
i
2019 - 07 - 28 04:00:00 '' ,
i
2019 - 07 - 28 06:00:00 '1 ,
dtype='datetime64[ns]',
i
i

In [31]: csv _file = open( path

'2019 - 07 - 27 23:00:00' >
'2019 - 07 - 28 01:00:00' i

'2019 - 07 - 28 03:00:00' i
'2019 - 07 - 28 05:00:00' f
'2019 - 07 - 28 07:00:00'
length = 5000, freq = 'H')

+ ' data.csv ', 'w ')

In [32]: header = ' date, nol,no2,no 3,no4,no5\ n '
In [33]: csv _ file.write( header)
Out[33]: 25

©

©

©

In [34]: for t _, (nol, no2, no3, no4, no5) in zip(t, a):

©

Операции ввода-вывода

289

,

,

{},{},{},{},{},{}\ n .format(t _, nol, no2, no3,
no4, no5) ©
csv _ file.write(s) ©
S

=

In [ 35 ] : csv _ file.close()

In [ 36 ] : ll $ path*
- rw - г - - г - - 1 yves staff 284757 Oct 19 12:11
/Users/yves/Temp/ data / data.csv

О Количество строк в наборе данных.

© Создание объекта ndarray, наполняемого случайными числами.
© Создание объекта Datetinelndex необходимой длины ( с часовым интервалом выборки ).

О Открытие файла для записи.

в

Создание строки заголовков ( названия столбцов ) и запись ее в качестве
первой строки файла.

© Построчное объединение двух массивов...
© ...и получение объектов str ...
© ...которые последовательно записываются в CSV-файл (с добавлением в
конец).

Обратная операция реализуется несложно. Сначала нужно открыть существующий CSV-файл, а затем построчно прочитать его содержимое с помощью метода readlineQ или readlinesQ объекта file.
In [ 37 ] : csv _file = open( path

+ ' data.csv ',

' г ')

О

In [ 38 ] : for i in range ( 5 ) : ©
print(csv _file.readlineQ , end = ' 1 )
date,nol,no2,no3, no4,no5
2019 - 01 - 01 00:00:00, - 0.0892, - 1.0508, - 0.5942,0.3367,1.508
2019 - 01 - 01 01:00:00,2.1046,3.2623,0.704, - 0.2651,0.4461
2019 - 01 - 01 02:00:00, - 0.0482, - 0.9221,0.1332,0.1192,0.7782
2019 - 01 - 01 03:00:00, - 0.359,- 2.4955,0.6164,0.712, - 1.4328

In [ 39 ] : csv _file.close()

290

Глава 9

_

In [40]: csv file

In [41]: content

=

=

open(path + 'data.csv ' , 'г *)

_

csv file.readlines()

О

©

In [42]: content[:5] ©
0ut[42]: ['date,nol,no2,no3,no4,no5\n',
'2019 01 01 00:00:00, 0.0892, 1.0508, 0.5942,0.3367,1.508\n',
'2019 01 01 01:00:00,2.1046 ,3.2623,0.704, 0.2651,0.4461\n ' ,
'2019 01 01 02:00:00, 0.0482,- 0.9221,0.1332,0.1192,0.7782\n ' ,
'2019 01 01 03:00:00, 0.359, 2.4955,0.6164,0.712, 1.4328\n']

- - - - -

-

-

-

-

-

-

_

In [43]: csv file.close()

О

Открытие файла для чтения(г).

© Построчное считывание содержимого файла с выводом на экран.

0 Считывание всего файла за один раз.

О

В результате формируется список, включающий все строки файла в виде
объектов st г .

Файлы формата CSV настолько распространены, что в стандартную библиотеку Python включен специальный модуль csv, упрощающий работу с ними.
Этот модуль содержит два объекта reader ( итераторы ), которые возвращают
либо список списков, либо список словарей.
In [44]: Import csv

In [45]: with open(path + ' data.csv ' , ' г ') as f:
csv reader = csv.reader(f) ©
lines = [line for line in csv reader]

_

_

In [46]: lines[:5] ©
0ut[46]: [[' date', ' nol' , 'no2' , ' no3 ' , 'no4', ' no5'],
['2019 01 01 00:00:00', ' 0.0892', ' 1.0508', ' 0.5942',
'0.3367', '1.508 '],
['2019 01 01 01:00:00' , ' 2.1046 ' , '3.2623', '0.704' ,
' 0.2651', '0.4461'],
[' 2019 01 01 02:00:00' , ' 0.0482', ' 0.9221', '0.1332',
'0.1192', '0.7782'],
[' 2019 -01 01 03:00:00' , ' 0.359 ' , ' 2.4955 ' , '0.6164' ,
'0.712' , ' 1.4328']]

- - - -

-

-

-

-

-

-

-

Операции ввода-вывода

291

.

I n [ 47 ] : w i t h o p e n ( p a t h + ' d a t a c s v ' , ' г 1 ) a s f :
c s v_ r e a d e r = c s v D i c t R e a d e r ( f ) ©
l i n e s = [ l i n e f o r l i n e i n c s v_r e a d e r ]

.

I n [ 4 8 ] : l i n e s [ :3 ] ©
O u t [ 48 ] : [ O r d e r e d D i c t ( [ ( ' d a t e ' , ' 2 0 1 9 0 1- 0 1 00: 00: 00 ' ) ,
( ' n o l ' , ' - 0 . 0 8 9 2 ' ),

-

( ' n o2 ' , ' - 1 . 0 5 0 8 ' ),
( ' n o3 ' , ' - 0 . 5 9 4 2 ' ) ,
( ' n o4 ' , ' 0 . 3 3 6 7 ' ) ,
( ' n o5 ' , ' 1 . 5 0 8 ' ) ] ) ,
O r d e r e d D i c t ( [ ( ' d a t e ' , ' 2 0 1 9 0 1 0 1 01: 00:00 ' ) ,
C n o l ' , ' 2 . 1 0 4 6 ' ),
( ' n o2 ' , ' 3 . 2 6 2 3 ' ),
( ' n o3 ' , ' 0 . 7 0 4 ' ) ,
( ' n o4 ' , ' - 0 . 2 6 5 1' ),
( ' n o5 ' , ' 0 . 4 4 6 1 ' ) ] ),
O r d e r e d D i c t ( [ ( ' d a t e ' , ' 2 0 1 9 - 0 1- 0 1 02: 00: 00 ' ) ,
C n o l ' , ' - 0 . 0 4 8 2 ' ),
( ' n o2 ' , ' - 0 . 9 2 2 1' ),
( ' n o3 ' , ' 0 . 1 3 3 2 ' ),
C n o4 ' , ' 0 . 1 1 9 2 ' ) ,
( ' n o5 ' , ' 0 . 7 7 8 2 ' ) ] ) ]

- -

I n [ 49 ] : ! r m

-f

$p a t h*

О Функция c s v . r e a d e r ( ) возвращает строки CSV-файла в виде списка.

© Функция c s v .D i c t R e a d e r ( ) возвращает строки CSV-файла в виде объектов O r d e r e d D i c t, которые являются частным случаем словаря .

Работа с реляционными базами данных
Python умеет работать с любыми реляционными С У Б Д и базами данных
NoSQL. В состав Python по умолчанию включена С У Б Д SQLite3 ( www s q l i t e
o r g). Мы воспользуемся ею, чтобы продемонстрировать принципы работы с

.

.

реляционными базами данных3.
3

.
.

Обзор интерфейсов доступа к базам данных в Python доступен по адресу https: / / wiki
python org /rnoin / DatabaseInterfaces. Вместо того чтобы работать с реляционными базами данных напрямую, лучше применять ORM-библиотеки наподобие SQLAlchemy (www
sqlalcheny org). Они добавляют уровень абстракции, позволяющий писать объектно-ориентированный код в стиле Python. Кроме того, они дают возможность переключаться на разные серверные СУБД путем изменения конфигурации.

.

.

292

Глава 9

In [50]: Import sqlite3 as sq 3
In [51]: con

= sq 3 . connect ( path

In [52]: query

=

' CREATE TABLE

+ ' numbs . db ' )

О

©

numbs ( Date date , Nol real , No2 real ) '

In [53]: con . execute ( query ) ©
0 ut [ 53 ] : < sqlite 3 . Cursor at 0xl 02655fl 0 >
In [54]: con . commitQ
In

©

[55]: q = con . execute ©

In [56]: q ( ' SELECT * FROM sqlitejnaster ' ) . fetchall ( )
Out [ 56 ] : [ ( ' table ' ,

©

' numbs ' ,
' numbs ' ,

2,
' CREATE TABLE numbs ( Date date , Nol real , No 2 real ) ' ) ]

О Настройка подключения к базе данных. Если файла не существует, он
будет создан .

© SQL- запрос на создание таблицы с тремя столбцами4.

© Выполнение запроса...

0 ...и фиксация изменений.
© Определение короткого псевдонима для метода con . execute ( ) .

© Извлечение метаинформации о базе данных.
После установки подключения к базе данных и создания таблицы можно наполнить ее данными. Каждая строка таблицы будет содержать объект
datetime и два объекта типа float .
In [57]: import datetime
In

[58]: now = datetime . datetime . nowQ

q ( ' INSERT INTO numbs VALUES ( ? , ? , ? ) ’ , ( now , 0.12, 7.3))
Out[58]: < sqlite 3 . Cursor at Oxl 02655 f 80>

4

©

Синтаксис языка SQLite3 детально описан на официальном сайте (https : / / www . sqlite . огд /
lang . html ) .

Операции ввода -вывода

293

In [59]: np.random.seed(100)
In [60]: data = np.random.standard _ normal((10000, 2)).round(4)

©

In [61]: XXtime
for row in data: ©
now = datetime.datetime.nowQ
q('INSERT INTO numbs VALUES(?, ?, ?)', \
(now, row[0], row[l]))
con.commit()
CPU times: user 115 ms, sys: 6.69 ms, total: 121 ms
Wall time: 124 ms
In [62]: q('SELECT * FROM numbs ' ).fetchmany(4) 0
Out[62]: [('2018 - 10 - 19 12:11:15.564019', 0.12, 7.3),
('2018 - 10 - 19 12:11:15.592956', - 1.7498, 0.3427),
('2018 - 10 - 19 12:11:15.593033', 1.153, - 0.2524),
('2018 - 10 - 19 12:11:15.593051', 0.9813, 0.5142)]

In [63]: q('SELECT * FROM numbs WHERE nol > 0.5').fetchmany(4)
0ut[63]: [('2018 - 10 - 19 12:11:15.593033', 1.153, - 0.2524),
('2018 - 10 - 19 12:11:15.593051', 0.9813, 0.5142),
( ' 2018 - 10 - 19 12:11:15.593104', 0.6727, - 0.1044),
( ' 2018 - 10 - 19 12:11:15.593134', 1.619, 1.5416)]
In [64]: pointer = q( ' SELECT * FROM numbs ' )

©

In [65]: for i in range( 3):
print( pointer.fetchoneQ ) ©
('2018 - 10 - 19 12:11:15.564019', 0.12, 7.3)
('2018 - 10 - 19 12:11:15.592956 ', - 1.7498, 0.3427)
('2018 - 10 - 19 12:11:15.593033', 1.153, - 0.2524)
In [66]: rows = pointer.fetchallQ ©
rows[:3]
Out[66]: [('2018 - 10 - 19 12:11:15.593051', 0.9813, 0.5142),
('2018 - 10 - 19 12:11:15.593063', 0.2212, - 1.07),
('2018 - 10 - 19 12:11:15.593073', - 0.1895, 0.255)]

О Добавление строки в таблицу numbs.

© Создание объекта ndarray, содержащего случайные числа.
© Прокрутка содержимого объекта ndarray.
294

Глава 9

©

О Извлечение нескольких строк из таблицы.

0 То же самое, но с условием для столбца nol.
© Создание объекта-указателя...

0 ...выступающего в качестве генератора.
© Извлечение всех остальных строк.
Если таблица больше не нужна, ее можно удалить из базы данных.
In [ 67 ] : q ( ' DROP TABLE IF EXISTS nunbs ' )
Out [ 67 ] : < sqlite 3 . Cursor at 0xll87a 7420 >

О

_

In [ 68 ] : q ( ' SELECT * FROM sqlite naster ' ) . fetchall ( )
0 ut [ 68 ] : [ ]

In [ 69 ] : con . close ( )
In [ 70 ] : ! rm

-f

$ path*

©

©
©

О Удаление таблицы из базы данных.

© Теперь в базе данных отсутствуют таблицы.
© Закрытие подключения к базе данных.

0 Удаление файла базы данных с диска.



Реляционные базы данных достаточно обширная тема, к тому же слиш ком сложная, чтобы полноценно рассмотреть ее здесь. Вам достаточно знать

следующее:

• Python прекрасно работает практически с любыми базами данных;
• синтаксис SQL зависит от конкретной СУБД, а все остальное реализует ся на уровне Python.

В последующих разделах будет приведен ряд других примеров работы с
SQLite 3.

Считывание и запись массивов NumPy
Библиотека NumPy располагает удобными и производительными функциями для считывания и записи объектов ndarray. Это может пригодиться, на пример, когда необходимо преобразовать объекты NumPy типа dtype в один
Операции ввода -вывода

295

из типов данных, специфичных для конкретной СУБД ( такой, как SQLite3).
Чтобы продемонстрировать эффективность NumPy, возьмем за основу при мер из предыдущего раздела.
На этот раз, в отличие от библиотеки pandas, мы воспользуемся функци ей np . arangeQ , чтобы сгенерировать объект ndarray, включающий объект
datetime.
In [ 71] : dtimes = np . arange ( ' 2019 - 01 - 01 10 : 00 : 00 ' , \
' 2025 - 12 - 31 22 : 00 : 00 ' , dtype = ' datetime 64[ m ] ' )

О

In [ 72 ] : len ( dtimes )
Out[ 72 ] : 3681360
In [ 73 ] : dty

=

np . dtype ( [ ( ' Date ' , ' datetime 64[ m ] ' ) ,
( ' Nol ' , ' f ' ) , ( ' No 2 ' , ' f ' ) ] )

In [ 74 ] : data = np . zeros ( len ( dtimes ) , dtype =dty )
In [ 75 ] : data [ ' Date ' ] = dtimes
In [ 76 ] : a

=

©

©

©

np . random . standard _ normal ( ( len ( dtimes ) , 2 ) ) . round ( 4 )

In [ 77 ] : data [ ' Nol ' ]
data[ ' No2 ' ]
In [ 78 ] : data . nbytes
0ut [ 78 ] : 58901760

©

a[ : , 0 ] ©
= a[ : , 1 ] ©

=

©

О Создание объекта ndarray для хранения данных типа datetime.

© Создание специального объекта структурированного массива.
© Создание объекта ndarray для хранения структурированного массива.

©

Заполнение столбца Date.

© Генерирование случайных чисел...
© ...добавляемых в столбцы Nol и No 2.
© Размер структурированного массива в байтах.
Операции сохранения объектов ndarray оптимизированы и выполняются
очень быстро. В данном случае массив размером почти 60 Мбайт сохраняется

296

Глава 9

за долю секунды (на SSD-диске). Для сохранения более крупного объекта
ndarray размером 480 Мбайт потребуется примерно полсекунды5.

In [79]: %time np.save(path + 'array ', data) О
CPU times: user 37.4 ms, sys: 58.9 ms, total: 96.4 ms
Wall time: 77.9 ms
In [80]: ll $path* ©
- rw - г - - г -- 1 yves staff 58901888 Oct 19 12:11
/Users/yves/Temp/data/array.npy
In [81]: %time np.load(path + 'array.npy ') ©
CPU times: user 1.67 ms, sys: 44.8 ms, total: 46.5 ms
Wall time: 44.6 ms
('2019 01 О1Т10:00', 1.5131, 0.6973),
0ut[81]: аггау([
('2019 01 O1T10:01', - 1.722 , -0.4815),
('2019 01 01110:02', 0.8251, 0.3019), ...,
('2025 12 31T21:57', 1.372 , 0.6446),
('2025 12 31121:58', 1.2542, 0.1612),
('2025 12 31121:59 ' , 1.1997, 1.097 )],
dtype=[('Date ' , ' 0 AND No2
res = np.array(q(query).fetchallQ).round(3) ©
CPU times: user 639 ms, sys: 64.7 ms, total: 704 ms
Wall time: 702 ms
In [99]: res = res[::100] ©
plt.figure(figsize=(10, 6))
plt.plot(res[:, 0], res[:, 1], ' ro')

<

0'

©

О Вставка всего набора данных в таблицу одной командой .

© Извлечение всех строк таблицы одной командой .
© Выборка строк и преобразование их в объект ndarray .
© Визуализация результатов выполнения запроса.
0.0

а•

#

• *
»*) • »%V и
• ••
#

-0.5

-1.0

; • •• • •••

••

-1.5



-2.0
-2.5

.•


-3.0

-3.5

Г *••*

,•

:

9

:• •









/

о

1

2

3

4

Рис. 9.1. Диаграмма рассеяния для результатов запроса ( выборка )

Операции ввода-вывода

301

Импорт данных из реляционных баз данных
Все же более эффективный подход заключается в импорте таблиц или результатов запроса с помощью библиотеки pandas. Когда вся таблица находится в памяти, аналитические запросы выполняются намного быстрее, чем в
случае обращения к файлам базы данных.
Чтение таблицы с помощью библиотеки pandas занимает примерно столько же времени, сколько занимает загрузка данных в объект ndarray библиотеки NumPy. В этом случае узким местом оказывается сама база данных.

In [ 180] : %time data = pd .read _ sql( 1 SELECT * FROM numbers ', con) О
CPU times: user 2.17 s, sys: 180 ms, total: 2.35 s
Wall time: 2.32 s
In [ 101 ] : data.head()
Out [ 101] :
Nol
0
1
2
3
4

0.4918
0.4516
0.1629
1.3064
- 0.1148

No2
1.3707
1.4445
- 0.8473
0.9125
- 1.5215

No3
0.1370
0.0555
- 0.8223
0.5142
- 0.7045

No4
0.3981
- 0.0397
- 0.4621
- 0.7868
- 1.0042

No5

- 1.0059
0.4400

- 0.5137
- 0.3398
- 0.0600

О Считывание всех строк таблицы в объект Data Frame с именем data .
Как только данные оказываются в памяти, их обработка резко ускоряется.
Производительность часто возрастает на порядок или даже больше. Кроме
того, с помощью библиотеки pandas можно составлять более сложные запросы, хотя они, конечно же, не могут служить полноценной заменой запросам
SQL. На рис. 9.2 визуализируются результаты выполнения запроса с составным условием.

In [ 102] : %time data[(data[ Nol ' ] > 0) & (data[ ' No 2'] < 0)].head() О
CPU times: user 47.1 ms, sys: 12.3 ms, total: 59.4 ms
Wall time: 33.4 ms
1

Out [ 102 ] :
2
5
8
10
11

302

Глава 9

Nol
0.1629
0.1893
1.4784
0.8092
0.9065

No3
No2
- 0.8473 - 0.8223
- 0.0207 - 0.2104
- 0.3333

-0.9899
- 0.7757

- 0.7050
1.0364
- 0.9267

No4
- 0.4621

0.9419
0.3586
- 1.0453
0.7797

No5
- 0.5137
0.2551

- 0.3937
0.0579
0.0863

In [ 103 ] : mime
q = ' ( Nol < - 0.5 | Nol > 0.5 ) & ( No 2 < - 1 | No 2 > 1) '
res = data [[ ' Nol ' , ' No 2 ' ] ] query ( q ) ©
CPU times : user 95.4 ms, sys : 22.4 ms, t o t a l: 118 ms
Wall time : 56.4 ms

©

.

.
.

I n [ 104 ] : p l t figure ( figsize= ( 10, 6 ) )
p l t plot ( res [ ' Nol ' ] , res [ ' No 2 ' ] ,

' r o ' );

О Два условия отбора записей.
Четыре условия отбора записей.

Q

• •
••

Л*#



1

• •

•л•

••

«

о

_

•I

%

••

4

%

—4

-2

0

2

1

Рис. 9.2. Диаграмма рассеяния для результатов запроса ( выборка)

Как и ожидалось, применение инструментов библиотеки pandas, обрабатывающих данные непосредственно в памяти, дает значительный прирост в
производительности, при условии, что библиотека позволяет воспроизвести

соответствующую инструкцию SQL.
Но это не единственное преимущество библиотеки, ведь она тесно интегрирована с другими пакетами, включая РуТаЫе, с которым мы познакомимся далее. Пока достаточно знать, что применение пакетов в связке позволяет
существенно ускорить выполнение операций ввода-вывода, как показано в
следующем примере.
Операции ввода-вывода

303

.

In [ 185 ] : h 5 s = pd HDFStore( filename +

' . h5s ' , ' w ' ) О

In [ 106 ] : %time h5 s [ ' d a t a ' ] = data ©
CPU times : user 46.7 ms, sys : 47.1 ms, total: 93.8 ms
Wall time: 99.7 ms
In [ 107 ] : h 5 s ©
Out [ 107 ] :

2

• •г

4

-

•• •

:
-2



U

4

%

1

Рис. 9.6. Диаграмма рассеяния для столбцов

(

и

"1

к

Быстрые запросы
Библиотеки pandas и PyTables способны обрабатывать достаточно
сложные SQL-подобные запросы. Обе библиотеки оптимизированы для выполнения таких операций. Несмотря на наличие определенных ограничений по сравнению с реляционными базами дан ных, для большинства аналитических и финансовых приложений
эти ограничения совершенно несущественны.

Следующий пример позволяет убедиться в том, что работа с объектами
Table библиотеки PyTables мало чем отличается от управления объектами
NumPy и pandas с точки зрения как синтаксиса, так и производительности.

Операции ввода -вывода

315

In [156]: %%time
values = tab[:]
[' No3']
print(' Max %18.3f' % values.nax()
)
print(' Mean %18.3f' % values.nean())
print('Min %18.3f' % values.minQ)
print('Std %18.3f' % values.stdQ)
Max
5.224
Mean
0.000
Min
- 5.649
Std
1.000
CPU times: user 163 ms, sys: 70.4 ms, total: 233 ms
Wall time: 234 ms
In [157]: %/
°otime
res = [(row[' Nol '], row['No2']) for row in
tab.where('((Nol > 9800)|(Nol < 200)) \
& ((No2 > 4500)& (No2 < 5500))')]
CPU times: user 165 ms, sys: 52.5 ms, total: 218 ms
Wall time: 155 ms

In [158]: for г in res[:4]:
print(r)
(91, 4870)
(9803, 5026)
(9846, 4859)
(9823, 5069)
In [159]: %%time

res

= [(rowf ' Nol '], row['No2']) for row in
tab.where((Nol == 1234) &(No2 > 9776)')]
1

CPU times: user 58.9 ms, sys: 40.5 ms, total: 99.4 ms
Wall time: 81 ms

In [160]: for г in res:
print(r)

(1234, 9841)
(1234, 9821)
(1234, 9867)
(1234, 9987)
(1234, 9849)
(1234, 9800)

316

Глава 9

Работа со сжатыми таблицами



Основное преимущество PyTables
поддержка сжатия, которое применяется не только для экономии места на диске, но и для ускорения операций
ввода -вывода. За счет чего это достигается ? Если скорость каналов ввода- вы вода является узким местом и центральный процессор способен быстро обрабатывать сжатые потоки данных, то применение сжатия приводит к вы игрышу в производительности. В следующих примерах данные хранятся на
SSD - накопителе, поэтому заметных преимуществ по скорости не наблюдает ся, зато можно убедиться в отсутствии каких-либо недостатков, связанных
с применением сжатия.
In [ 161 ] : filename
In [ 162 ] : h 5 c

=

path +

' pytabc . h 5 '

= tb . open_ file ( filename , ' w ' )

In [ 163 ] : filters

In [ 164 ] : tabc

In [ 165 ] : query

= tb . Filters ( complevel = 5 , О
complib= ' blosc ' ) ©
' ints _floats ' , sarray ,
title = ' Integers and Floats ' ,
expectedrows = rows , filters = filters )

= h 5c . create_ table ( ' /

=

' ( ( No 3 < - 0.5 ) | ( No 3 > 0.5 ) ) & ( ( No 4 < - 1 )
( No4 > 1 ) ) '

©

In [ 166 ] : iteratorc

=

In [ 167 ] : % time res

= [ ( row[ ' No 3 ' ] , row[ ' No 4 ' ] )

tabc . where ( query )

for row in
iteratorc ]
CPU times : user 300 ms , sys : 50.8 ms , total : 351 ms
Wall time : 311 ms

0

= np . array ( res )
res [ : 3 ]
0ut [ 168 ] : array ( [ [ 0.7694 , 1.4866 ] ,
[ 0.9201 , 1.3346 ] ,
[ 1.4701 , 1.8776 ]] )
In [ 168 ] : res

О Параметр complevel задает степень сжатия в диапазоне от 0 ( без сжатия )
до 9 (максимальное сжатие) .

0 Применение оптимизированной системы сжатия Blosc ( http : / / blosc .
org / ) .
Операции ввода-вывода

317

© Создание объекта итератора для запроса.
О Извлечение результатов запроса с помощью спискового включения.
Сжатый объект Table создается и обрабатывается чуть медленнее, чем
несжатый. Теперь проверим, с какой скоростью выполняется загрузка данных
в объект ndarray.

_

.

: %time arr non = tab read ( ) О
In [169]
CPU tines : user 63 ns, sys : 78.5 ns , total: 142 ns
Wall tine : 149 ns

_

.

In [170]: tab size_ on disk
Out[170]: 100122200

_

In [171]: arr non . nbytes
0ut[171]: 100000000

.

In [172]: %tine arr _con = tabc readQ ©
CPU tines : user 106 ns, sys : 55.5 ns , total: 161 ns
Wall tine: 173 ns
In [173]: tabc . size_ on_disk
0ut[173]: 41306140

.

In [174]: arr _con nbytes
Out[174]: 100000000
In [175]: ll $path*

©

rw - г - - г - - 1 yves staff 200312336 Oct 19 12 :12
/Users / yves / Tenp / data / pytab h 5
- rw - г - - г - - 1 yves staff 41341436 Oct 19 12:12
/Users / yves / Tenp / data / pytabc h5

.

.

.

In [176]: h5 c close ( )

0

О Считывание несжатого объекта tab.

© Считывание сжатого объекта tabc.
©

Сравнение размеров файлов: сжатая таблица занимает значительно
меньше места.

0 Закрытие файла базы данных.

318

Глава 9

Эти примеры наглядно показывают, что особой разницы по скорости между сжатыми и несжатыми объектами Table нет. А вот размеры файлов на диске могут существенно отличаться ( в зависимости от структуры данных), что
дает целый ряд преимуществ:

• уменьшение стоимости хранения данных;
• уменьшение стоимости резервного копирования данных;



уменьшение сетевого трафика;

• повышение скорости передачи данных по сети ( ускорение обмена дан ными с сервером );

• устранение узких мест, связанных с пропускной способностью каналов

ввода - вывода, за счет выполнения определенных операций центральным процессором.

Работа с массивами
Как было показано в начале главы, библиотека NumPy обладает встроен ными оптимизированными средствами чтения и записи данных в объекты
ndarray. Пакет PyTables тоже реализует достаточно быстрые и эффективные
операции сохранения и загрузки объектов ndarray, а поскольку он основан
на иерархической структуре базы данных, это обеспечивает ряд дополнительных преимуществ.
In [ 177 ] : %%tine
arr int = h 5 create array ( ' / ' , ' integers ' , ran int ) О
arr flo = h 5 create array ( ' / ' , ' floats ' , ran flo ) ©
CPU tines : user 4.26 ns , sys : 37.2 ns , total : 41.5 ns
Wall tine : 46.2 ns

_
_

.
.

_

__

_

In [178 ] : h 5 ©
0 ut [ 178 ] : File ( filenane = / Users / yves / Tenp / data / pytab h 5 , title= ' ' ,
node = ' w ' , root uep = ' / ' , filters = Filters ( conplevel =0 ,
shuffle = False , bitshuffle = False , fletcher 32= False ,
least significant digit = None ) )
(
) ''
RootGroup
/
/ floats ( Array ( 2000000 , 2 ) ) ' '
aton : = Float 64Aton ( shape = ( ) , dflt =0.0 )
naindin : = 0
flavor : = ' nunpy '
byteorder : = ' little '
chunkshape : = None

.

_

_

_

Операции ввода-вывода

319

( Аггау ( 2 ООО0О 0, 2 ) ) ' '
atom := Int 64Atom( shape=( ) , dflt=0)
maindim := 0
flavor := ' numpy '

/ integers

byteorder := ' little '
chunkshape := None
/ints floats ( Table( 2000000, ) ) ' Integers and Floats '
description := {
" Date " : StringCol( itemsize= 26, shape= ( ) , dflt=b ' ' , pos=0 ) ,
" Nol " : Int 32Col( shape= ( ) , dflt=0, pos=l) ,
" No 2 " : Int 32Col( shape= ( ) , dflt =0, pos= 2 ) ,
" No 3 " : Float 64Col( shape=( ) , dflt=0.0, pos=3 ) ,
" No 4 " : Float 64Col( shape=( ) , dflt=0.0, pos=4) }
byteorder := ' little '
chunkshape := ( 2621,)

_

In [ 179 ] : l l $path*
- rw - г - - г - - 1 yves staff 262344490 Oct 19 12 :12
/Users / yves / Temp / data / pytab h5
41341436 Oct 19 12: 12
rw г - - г - 1 yves staff
/ Users / yves / Temp / data / pytabc h5

- -

-

.

.

.

In [ 180 ] : h 5 close ( )
In [ 181 ] : ! rm

-f

$path*

О Сохранение объекта ndarray с именем ran_int.

© Сохранение объекта ndarray с именем ran_ flo.
© Изменения отражаются в описании объекта.
Непосредственная запись таких объектов в базу данных HDF5 выполняется намного быстрее, чем построчная запись значений в объект Table или
структурированный массив ndarray.
Хранилища данных в формате HDF5

(

320

Формат иерархической базы данных HDF5 служит прекрасной аль тернативой реляционным базам данных, особенно когда дело ка|сается обработки структурированной числовой и финансовой информации. Как при автономном применении пакета PyTables, так и
в связке с библиотекой pandas можно добиться практически мак симальной для используемого оборудования производительности
операций чтения и записи данных.
Глава 9

Вычисления в условиях нехватки памяти



Пакет PyTables поддерживает операции ООМ ( Out -of - Memory вне опера тивной памяти ), что позволяет реализовать обработку массивов, не помеща ющихся в памяти компьютера. Рассмотрим следующий пример, основанный
на использовании класса ЕАггау. Такого рода объекты могут расширяться в
одном из измерений ( построчно), тогда как количество столбцов ( элементов
каждой записи ) должно оставаться неизменным.

In [182]: filename
In [183]: h5

In [184]: n

=

=

In [185]: ear

=

path

+ 'earray.h 5 '

_

tb.open file(filename, 'w ')
500

=

О

_

h5.create earray(' / ', ' ear ' , ©
atom=tb.Float64Atom(),
shape=(6, n)) ©

©

In [186]: type(ear)
0ut[186]: tables.еаггау.ЕАггау

_

In [187]: rand = np.random.standard normal((n, n)) ©
rand[:4, : 4 ]
0ut[187]: array([[- 1.25983231, 1.11420699, 0.1667485 , 0.7345676 ],

[- 0.13785424, 1.22232417, 1.36303097, 0.13521042],
[ 1.45487119, 1.47784078, 0.15027672, 0.86755989],
[-0.63519366, 0.1516327 , -0.64939447, - 0.45010975]])

-

In [188]: %%time
for in r a n g e ( 7 5G ) :
ear.append(rand) ©
ear.flushQ
CPU times: user 814 ms, sys: 1.18 s, total: 1.99 s
Wall time: 2.53 s

_

In [189]: ear
0ut[189]: /ear (EArray(375000, 500)) "
atom := Float64Atom(shape=(), dflt=0.O)
maindim := 0
flavor := 'numpy '
byteorder := ' little'
chunkshape := (16, 500)

Операции ввода-вывода

321

In [ 190 ] : ear . size _on _disk
Out [ 190 ] : 1500032000

О Фиксированное количество столбцов.

© Путь размещения и внутреннее имя объекта ЕАггау .
© Атомарный объект dtype для отдельных значений.
© Структура объекта при инициализации (отсутствие строк, п столбцов ).

© Объект ndarray , заполняемый случайными числами...
© ...которые добавляются многократно.
Для выполнения ООМ - вычислений, в которых не задействуются стати стические функции, нужен еще один объект ЕАггау такой же структуры. В
пакет PyTables включен специальный модуль Ехрг , основанный на библиотеке пипехрг ( https : / / nunexpr . readthedocs . io / ) , который предназначен для
эффективной работы с числовыми выражениями. В следующем примере модуль Ехрг применяется для вычисления математического выражения, представленного уравнением 9.1, по отношению ко всему объекту ЕАггау .

Уравнение 9.1 . Пример математического выражения

, = 3 sin(*) + Vj4
Результаты сохраняются в объекте out типа ЕАггау , а само выражение вы числяется поблочно.
In [ 191 ] : out

= h 5 . create _earray ( / ' , ' out ' ,
aton= tb . Float 64Atom ( ) ,
1

shape = ( 0 , n ) )

_

In [ 192 ] : out . size _ on di. sk
Out [ 192 ] : 0
In [ 193 ] : ехрг

= tb . Expr ( ' 3

_

* sin ( ear ) + sqrt ( abs ( ear ) ) ' )

_

In [ 194 ] : ехрг . set output ( out , append mode = True )

©

In [ 195 ] : %tine expr . evalQ ©
CPU tines : user 3.08 s , sys : 1.7 s , total : 4.78 s
Wall tine : 4.03 s
Out [ 195 ] : / out ( EArray ( 375 O00 , 500 ) ) "
aton : = Float 64Aton ( shape = ( ) , dflt =0.0 )

322

Глава 9

©

maindim := 0
flavor := ' nunpy'
byteorder := ' little '
chunkshape := (16, 500)
In [196]: out.size _on _ disk
0ut[196]: 1500032000
In [ 197]: out[0, :10]
0ut[197]: array([ - l.73369462, 3.74824436, 0.90627898, 2.86786818,
1.75424957, - 0.91108973, - 1.68313885, 1.29073295,
- 1.68665599, - 1.71345309])
In [198]: %tine out _ = out.readQ О
CPU tines: user 1.03 s, sys: 1.1 s, total: 2.13 s
Wall tine: 2.22 s
In [ 199 ] : out _[ Q, : 10 ]
0ut [ 199 ]: array([ - l.73369462, 3.74824436, 0.90627898, 2.86786818,
1.75424957, - 0.91108973, - 1.68313885, 1.29073295,
- 1.68665599, - 1.71345309])

О Преобразование строкового выражения в объект Ехрг.
© Вывод результатов в объект out типа ЕАггау.
© Вычисление выражения.
© Загрузка в память всего объекта ЕАггау.
Если учесть, что расчеты выполняются не в оперативной памяти , то ско рость можно считать вполне приемлемой , тем более что пример запущен на
обычном компьютере. Для сравнения оценим производительность, достигаемую с помощью модуля numexp ( рассматривается в главе 10), который выпол няет все операции в памяти . Как видите, процесс ускоряется , но не на порядок.
In [200]: inport numexpr as ne

О

In [ 201 ] : ехрг = ' 3 * sin(out _)

+

In [ 202 ] : ne.set _ nun _threads( l)
Out[202]: 4

sqrt( abs(out _))'

©

©

In [ 203 ] : %tine ne.evaluate(expr)[3, : 10 ] ©
CPU tines: user 2.51 s, sys: 1.54 s, total: 4.05 s

Операции ввода-вывода

323

Wall time: 4.94 s
Out [ 283]: array([ - l.64358578, 0.22567882, 3.31363043, 2.50443549,
4.27413965, - 1.41600606, - 1.68373023, 4.01921805,
- 1.68117412, - 1.66053597])
In [ 204 ] : ne.set _ num _threads(4)
Out[ 204 ] : 1

©

In [ 205 ] : %time ne.evaluate(expr)[8, :10 ] ©
CPU times: user 3.39 s, sys: 1.94 s, total: 5.32 s
Wall time: 2.96 s
Out [ 205 ]: array([ - l.64358578, 0.22567882, 3.31363043, 2.50443549,
4.27413965, - 1.41600606, - 1.68373023, 4.01921805,
- 1.68117412, - 1.66053597])
In [ 286 ] : h 5.close()

In [ 207 ] : ! rm - f $path*

О Импорт модуля, предназначенного для вычисления математических выражений в оперативной памяти.

© Числовое выражение, представленное в строковом виде.
© Задание одного потока вычислений.

0 Вычисление выражения в оперативной памяти в одном потоке.
© Увеличение количества потоков до четырех.
© Вычисление выражения в оперативной памяти в четырех потоках.

Ввод и вывод данных с помощью TsTables
Пакет TsTables использует объекты PyTables для создания высокопроизводительного хранилища временных рядов. Основной сценарий его применения
однократная запись, многократное чтение. Такой подход типичен для
финансовых приложений, где данные поступают в режиме реального времени
или асинхронно и сохраняются на диске с целью последующего анализа. Это
может происходить, например, в программе тестирования торговой стратегии на исторических данных, где из временного ряда приходится регулярно
извлекать различные поднаборы. Важно, чтобы считывание таких данных
происходило с минимальными задержками.



324

Глава 9

Исходные данные
Как всегда, сначала необходимо сгенерировать тестовый набор данных, до статочно большой для демонстрации преимуществ пакета TsTables. В следую щем примере создаются три достаточно длинных временных ряда, получаемых путем моделирования геометрического броуновского движения (об этом
мы поговорим в главе 12).
In [ 208 ] : no = 5000000
со = 3 ©
interval = 1.
vol = 0 . 2 О

О

/

( 12 * 30 * 24 * 60 )

©

In [ 209 ] : m i n e
гп = пр . random . standard_nornal ( ( no , со ) ) ©
гп [ 0 ] = 0.0 ©
paths = 100 * np . exp ( np . cunsun ( ~ 0.5 * vol ** 2 * interval +
vol * np . sqrt ( interval ) * rn , axis=0 ) )
paths [ 0 ] = 100 ©
CPU tines : user 869 ns , sys : 175 ns , total : 1.04 s
Wall tine : 812 ns

©

О Количество временных периодов.

© Количество временных рядов.

© Временной интервал в виде доли года.
© Волатильность.

© Случайные числа с нормальным распределением вероятностей.
© Обнуление исходного набора случайных чисел.
© Моделирование по методу Эйлера.
© Начальные значения траекторий равны 100.
Поскольку пакет TsTables хорошо работает с объектами DataFrame библио теки pandas, данные приводятся именно к такому виду (рис. 9.7).
In [ 210 ] : dr = pd . date _ range ( ' 2019 - 1- 1 ' , periods =no , freq= ' ls ' )
In [ 211] : dr [ - 6 : ]
0ut [ 211 ] : Datetinelndex ( [ ' 2019 - 02 - 27 20 : 53 : 14 ' ,
' 2019 - 02 - 27 20 : 53 : 16 ' ,

' 2019 - 02 - 27
' 2019 - 02 - 27

20 : 53 : 15 ' ,
20 : 53 : 17 ' ,

Операции ввода-вывода

325

' 2019 - 02 - 27 20:53:18', '2019 - 02- 27 20:53:19'],
dtype='datetime64[ns]', freq ='S ')

In [212]: df

= pd.DataFrame(paths, index=dr, columns=['tsl', 'ts2', \
'ts3'])

In [213]: df.infoQ

Datetimelndex: 5000000 entries, 2019 01 01 00:00:00 to
2019 02 27 20:53:19
Freq: S
Data columns (total 3 columns):

- - -

float64
tsl
float64
ts2
float64
ts3
dtypes: float64(3)
memory usage: 152.6 MB
In [214]: df.headQ
Out[214]:
tsl

2019 - 01- 01
2019 - 01- 01
2019 - 01 - 01
2019 - 01- 01
2019 - 01- 01

00 : 00 : 00
00 : 00 : 01
00 : 00 : 02
00 : 00 : 03
00 : 00 : 04

100.000000
100.018443
100.069023
100.086757
100.105448

ts 3
ts 2
100.000000 100.000000
99.966644 99.998255
100.004420 99.986646
100.000246 99.992042
100.036033 99.950618

In [215]: df[::108000]. plot(figsize=(10, 6));

Хранение данных
Пакет TsTables хранит финансовые временные ряды в виде специальной
блочной структуры, позволяющей быстро извлекать произвольные поднаборы на основе заданного временного интервала. Для этого в пакет добавлена
функция create ts() . В следующем примере типы данных столбцов таблицы
определяются с помощью класса IsDescription из пакета PyTables.

_

In [216]: import tstables as tstab
.

_

In [217]: class ts desc(tb.IsDescription):
timestamp = tb.Int64Col(pos=0)
tsl = tb.Float64Col(pos=l) ©
ts2 = tb.Float64Col(pos=2) ©
ts3 = tb.Float64Col(pos=3) ©

326

Глава 9

О

tsl

ts 2
ts 3

300

250

200

150

100

50

0

31

07

21

14

28

Jan

18

11

04

25
Маг

Feb

2019

Рис . 9.7. Графики выбранных точек временного ряда

_

In [ 218 ] : h5

=

tb.open file(path

In [ 219 ] : ts

=

h5.create ts(' /', 'ts', ts desc)

+ ' tstab.h5', ' w')

_

_

©

©

In [ 220 ] : %time ts.append(df) ©
CPU times: user 1.36 s, sys: 497 ms, total: 1.86 s
Wall time: 1.29 s

In [ 221] : type ( ts )
0ut[221]: tstables.tstable.TsTable
In [ 222 ]: Is - n $path
total 328472

- rw- r - - г - -

1 501 20 157037368 Oct 19 12:13 tstab.h5

О Столбец временных меток.
© Столбцы числовых данных.

© Открытие файла базы данных HDF5 для записи.

0 Создание объекта TsTable на основе объекта ts_desc.
0 Добавление данных из объекта DataFrame в объект TsTable.
Операции ввода -вывода

327

Извлечение данных
Запись данных с помощью пакета TsTables выполняется очень быстро, хоть
и зависит от используемого оборудования. То же самое касается и считыва ния данных в память. Удобнее всего то, что при этом возвращается объект
DataFrame ( рис. 9.8).

__ _ _

In [223]: read start dt = dt.datetine(20i9, 2, 1, 8, 8)
read end dt = dt.datetine(2019, 2, 5, 23, 59)

_

_ _

О

©

_ _

In [22A ]: %time rows = ts.read range(read start dt, read end dt)
CPU tines: user 182 ns, sys: 73.5 ns, total: 255 ns
Wall tine: 163 ns

©

In [225]: rows.info()©

Datetinelndex: 431941 entries, 2019 -02-01 00:00:00 to
2019 -02-05 23:59:00
Data colunns (total 3 colunns):
tsl
431941 non - null float64
ts2
431941 non - null float64
ts3
431941 non - null float64
dtypes: float64(3)
nenory usage: 13.2 MB
In [226]: rows.head О ©

0ut[226]:

2019-02-01
2019-02-01
2019-02-01
2019-02-01
2019 -02-01

00:00:00
00:00:01
00:00:02
00:00:03
00:00:04

tsl
52.063640
52.087455
52.084808
52.073536
52.056133

ts2
ts3
40.474580 217.324713
40.471911 217.250070
40.458013 217.228712

40.451408 217.302912
40.450951 217.207481

In [227]: hS.closeQ
In [228]:(rows[::580] / rows.iloc[8]).plot(figsize=(10, 6));

О

Начало интервала.

© Конец интервала.

_

© Функция ts.read range() возвращает объект DataFrame, соответствуюО
328

щий заданному интервалу.
Объект DataFrame содержит несколько сотен тысяч строк данных.

Глава 9

tsl

1.20

К2

ts'A

1.15

1.10

1.05

1.00

0.95

0.90

0.85
02

01

03

04

05

()(

,

Feb
2019

Puc. 9.8. Фрагмент финансового временного ряда ( нормализованный )
для заданного интервала

Чтобы нагляднее продемонстрировать производительность операций чтения данных в пакете TsTables, рассмотрим следующий пример, в котором из влекаются 100 фрагментов данных, охватывающих трехдневный интервал с
ежесекундной выборкой. На извлечение одного объекта DataFrame, содержа щего 345 600 строк данных, уходит менее десятой доли секунды.
In [ 229 ] : import random

. _
' tstab . h 5 ' ,
ts = h5 . root . ts . _ f _ get _ timeseries ( )

In [ 230 ] : h 5 = tb open file ( path +

'г')

In [ 231] :

О

In [ 232 ] : %%time
for _ in range ( lOO ) :

©

.

d = random randint (l, 24) ©
read_ start _ dt = dt datetime ( 2019, 2, d, 0, 0, 8 )
read end dt = dt datetime( 2819, 2, d + 3 , 23, 59, 59 )
rows = ts read range ( read_ start _dt, read_ end_dt )
CPU times : user 7.17 s, sys : 1.65 s, total: 8.81 s
Wall time: 4.78 s

_ _

In [ 233 ] : rows

.infoQ

_

.

_

.

.

©
Операции ввода-вывода

329

' pandas.core.frame.DataFrame ’ >
Datetimelndex: 345600 entries, 2019 02 04 00:00:00 to
2019 02 07 23:59:59
Data columns (total 3 columns):
tsl
345600 non null float64
ts2
345600 non - null float64
ts3
345600 non - null float64
dtypes: float64(3)
memory usage: 10.5 MB


In [ 19 ] : %time average _cyl(n)
CPU times: user 695 ms, sys: 4.31 ms, total: 699 ms
Wall time: 711 ms
Out[19 ]: 0.49997106194496155

In [ 20 ] : %timeit average _ cyl( n)
752 ms ± 91.1 ms per loop (mean

±

std. dev. of 7 runs,

1 loop each )

О Импорт модуля random из пакета Cython.

© Статическое объявление типов переменных n , i и s .
338

Глава 10

Производительность повысилась, но не столь существенно, как в случае па кета NumPy. Чтобы добиться от Cython более ощутимых результатов ( превосходящих даже версию Numba ) , нужно дополнительно оптимизировать код.
In [21]: %%cython
from libc.stdlib cimport rand
cdef extern from ' limits.h ':
int INT MAX ©
cdef int i

_

cdef float rn
for i in range(5):
rn = rand() / INT MAX
print(rn)

_

О

©

©

0.6792964339256287
0.934692919254303
0.3835020661354065
0.5194163918495178
0.8309653401374817

-

In [22]: %%cython a
from libc.stdlib cimport rand
cdef extern from ' limits.h ':
int INT MAX ©
def average cy2(int n):
cdef int i

_

©
©

_

cdef float s = 0
for i in range(n):
s += randQ / INT MAX ©
return s / n
0ut[22]:

_

_

In [23]: %time average cy2(n)
CPU times: user 78.5 ms, sys: 422 ps, total: 79 ms
Wall time: 79.1 ms
Out[23]: 0.500017523765564

_

In [24]: Xtimeit average cy2(n)
65.4 ms ± 706 ps per loop (mean ± std . dev. of 7 runs,
10 loops each)

О

Импорт генератора случайных чисел языка С.

© Импорт константы для масштабирования случайных чисел.
© Получение набора случайных чисел из интервала (0, 1) с нормальным
распределением и применение к ним масштабирования.

Производительность Python

339

average _cy 2 ( ) —
Как видите, улучшенная версия исходной функции
работает быстрее, чем версия, реализованная с помощью NumPy. Правда,
это потребовало написания дополнительного кода. Самое главное, что пакет
Cython управляет памятью намного эффективнее, чем пакет NumPy, сохраняя
эффективность оригинальной реализации с циклами.



ш

Cython = Python + С
Cython позволяет разработчикам самостоятельно определять степень оптимизации кода. Оптимальным решением будет начать
с чистого кода Python , добавляя в него элементы языка С только
по мере необходимости. Процесс компиляции тоже можно пара метризовать, чтобы дополнительно оптимизировать скомпилированную версию кода.

Алгоритмы
В этом разделе мы применим изученные выше методики к известным ма тематическим задачам и алгоритмам, которые часто используются для оценки

производительности.

Простые числа
Простые числа играют важную роль не только в фундаментальной математике, но и во многих прикладных областях, например в шифровании. Простым считается целое положительное число, большее единицы, которое делится без остатка на само себя и на 1. Его нельзя разложить на множители.
Поиск больших простых чисел
довольно трудоемкая задача ввиду их редкости, зато относительно несложно доказать, что число не является простым.
Нужно лишь найти множитель, на который число делится без остатка.



Python
Существует множество алгоритмов для проверки простоты числа. Ниже
приведено решение, которое не оптимально с алгоритмической точки зрения,
зато достаточно эффективно. Тем не менее проверка большого числа р 2 занимает много времени.
In [ 25 ] : def Is prim( I ):
0: return
if I % 2

==

false О

for i in range(3, int(I ** 0.5) + 1 , 2 ) :

340

Глава 10

©

if I % i
return True

== 0:
0

In [26]: n = int(le8 + 3)
n
Out[26]: 100000003

return False

©

©

_

In [27]: %tine is prine(n)
CPU tines: user 35 ps, sys: 0 ns, total: 35 ps
Wall tine: 39.1 ps
0ut[27]: False
In [28]: pi

= int(le8 + 7) ©

Pi
0ut[28]: 100000007

_

In [29]: %tine is prine(pl)
CPU tines: user 776 ps, sys: 1 ps, total: 777 ps
Wall tine: 787 ps
0ut[29]: True
In [30]: p2

=

100109100129162907G

_

In [31]: p2.bit length()
0ut[31]: 57

©

_

In [32]: %tine is prine(p2)
CPU tines: user 22.6 s, sys: 44.7 ns, total: 22.6 s
Wall tine: 22.7 s
Out[32]: True

О Если число четное, сразу же возвращается False.

© Цикл начинается с 3 и продолжается до квадратного корня из I плюс 1 с
шагом 2.

© При нахождении множителя возвращается False.

0 Если множитель не найден, возвращается Тгие.
© Проверяем относительно небольшие простые и составные числа.
© Большое простое число, проверка которого занимает много времени.

Производительность Python

341

Numba
Циклический алгоритм, реализованный в функции is_ prime ( ) , хорошо
оптимизируется за счет динамической компиляции с помощью пакета Numba.
Накладные расходы оказываются минимальными, а выигрыш в производительности существенным.
In [ 33 ] : is _ prime _ nb = numba . jit ( is _ prime )



_

In [ 34 ] : %time is_ prime nb ( n ) О
CPU times : user 87.5 ms , sys : 7.91 ms , total : 95.4 ms
Wall time : 93.7 ms
Out [ 34 ] : False
In [ 35 ] : %time is _ prime _ nb ( n ) ©
CPU times : user 9 ps , sys : le + 03 ns , total : 10 ps
Wall time : 13.6 ps
Out [ 35 ] : False

_

In [ 36 ] : %time is prime _ nb ( pl )
CPU times : user 26 ps , sys : 0 ns , total : 26 ps
Wall time : 31 ps
0ut[ 36 ] : True
In [ 37 ] : %time is_ prime _ nb ( p 2 ) ©
CPU times : user 1.72 s , sys : 9.7 ms , total : 1.73 s
Wall time : 1.74 s
Out [ 37 ] : True

О При первом вызове функции is_ prime_nb ( ) возникают накладные расходы, связанные с компиляцией.

© При втором вызове функции is__prime_ nb ( ) ускорение становится очевидным.

© В случае больших простых чисел скорость увеличивается на порядок.

Cython
Применить пакет Cython в нашем случае несложно. Даже без объявления
типов наблюдается существенный прирост производительности.
In [ 38 ] : %%cython

_

def is prime cy1(I):
if I % 2 == 0: return False
„„

342

Глава 10

:
for i in range(3, int(I ** 0.5)+ 1, 2)
if I % i == 0: return False
return True

_

In [39]: %timeit is prime ( pl)
394 ps ± 14.7 ps per loop ( mean ± std

.

.

dev of 7 runs,

1000 loops each )
In [40]: %timeit is _prime_cyl ( pl)
243 ps ± 6.58 ps per loop ( mean ± std dev of 7 runs,
1000 loops each )

.

.

Но настоящее ускорение достигается только при статической типизации.
В этом случае версия Cython может даже работать чуть быстрее, чем версия

Numba.

: %%cython
In [41]
def is prime cy 2 (long I) :

__

_

О

cdef long i О
if I % 2 == 0: return False
:
for i in range(3, int(I ** 0.5) + 1, 2)
if I % i == 0: return False
return

True

In [42]: %timeit is _prime_cy 2 ( pl)
87.6 ps ± 27.7 ps per loop ( mean ± std dev of 7 runs,
10000 loops each)

.

_

.

_

In [43]: %time is prime nb( p 2 )
CPU times : user 1.68 s, sys : 9.73 ms, total: 1.69 s
Wall time: 1.7 s
0ut [ 43 ] : True
In [44]: %time is _prime_ cy 2 ( p 2 )
CPU times : user 1.66 s , sys : 9.47 ms , total: 1.67 s
Wall time: 1.68 s
Out[44]: True

О Статическое объявление типов для переменных I n i.

Параллельные вычисления
Пока что все наши усилия по оптимизации кода были сосредоточены на
последовательной обработке данных. В тестах простоты имеет смысл одноПроизводительность Python

343

временно обрабатывать сразу несколько чисел. В этом нам поможет модуль
multiprocessing ( https : / / docs . python . org / 3 / library / multiprocessing .
html ) , позволяющий запускать несколько параллельных процессов Python.
В нашем случае применить его несложно. Сначала необходимо создать объект
mp . Pool для управления пулом процессов. Затем к пулу подключается выпол няемая функция, которой передается список проверяемых чисел.
In [ 45 ] : import multiprocessing as mp
In [ 46 ] : pool

= mp . Pool ( processes= 4 ) О

_

In [ 47 ] : %time pool . map ( is prime , 10 * [ pi ] ) ©
CPU times : user 1.52 ms , sys : 2.09 ms , total : 3.61 ms
Wall time : 9.73 ms
0ut[ 47 ] : [ True , True , True , True , True , True , True , True , True , True ]

_

In [ 48 ] : %time pool . map ( is _ prime nb , 16 * [ p 2 ] ) ©
CPU times : user 13.9 ms , sys : 4.8 ms , total : 18.7 ms
Wall time : 10.4 s
0ut[ 4S ] : [ True , True , True , True , True , True , True , True , True , True ]
In [ 49 ] : %time pool . map ( is _ prime _cy 2 , 18 * [ p 2 ] ) ©
CPU times : user 9.8 ms , sys : 3.22 ms , total : 13 ms
Wall time : 9.51 s
0ut[ 49 ] : [ True , True , True , True , True , True , True , True , True , True ]

О Инициализация объекта mp . Pool для управления пулом процессов.

© Подключение целевой функции и передача ей списка простых чисел.
Скорость вычислений возрастает очень сильно. Проверка большого
простого числа р 2 с помощью функции is_ prime ( ) , написанной на чистом
Python , заняла более 20 секунд, тогда как проверка десяти таких чисел с помощью функций is _ prime_ nb ( ) и is _ prime _cy 2 ( ) занимает около 10 секунд за
счет параллельного выполнения четырех процессов.

(

и

344

*4

I

Параллельные вычисления
К параллельной обработке следует прибегать при решении нескольких однотипных задач с разными входными значениями.
Ускорение вычислений может оказаться огромным при наличии
мощного многоядерного процессора и достаточного объема оперативной памяти. Модуль multiprocessing
это эффективный инструмент, включенный в состав стандартной библиотеки Python.



Глава 10

Числа Фибоначчи
Числа Фибоначчи можно генерировать с помощью простого алгоритма.
Первые два числа последовательности 0 и 1, а каждое следующее число равно сумме двух предыдущих: 0, 1, 1, 2, 3, 5, 8, 13, 21... В этом разделе мы рассмотрим две реализации алгоритма: рекурсивную и итеративную.



Рекурсивный алгоритм
Как и циклы, рекурсивные функции, написанные на чистом Python, рабо тают достаточно медленно. Такие функции вызывают сами себя множество
раз для получения конечного результата. Ниже приведена реализация ре курсивной функции fib_ rec _ pyl ( ) . В данном случае использование пакета
Numba мало что дает, в отличие от пакета Cython , показывающего существен ное ускорение за счет одной только статической типизации.

_ __

In [ 50 ] : def fib rec pyl ( n ) :
If n < 2 :
return n
else :
return fib_ rec _ pyl ( n

-

_ _

1 ) + fib rec pyl ( n - 2 )

In [ 51 ] : %time fib_ rec _ pyl ( 35 )
CPU tines : user 6.55 s , sys : 29 ns , total : 6.58 s
Wall tine : 6.6 s
0ut [ 51 ] : 9227465

_

In [ 52 ] : fib_ rec nb

= nunba . jit ( fib_ rec _ pyl )

_ _

In [ 53 ] : %tine fib rec nb ( 35 )
CPU tines : user 3.87 s , sys : 24.2 ns , total : 3.9 s
Wall tine : 3.91 s
Out [ 53 ] : 9227465
In [ 54 ] : %%cython
def fib _ rec _ cy ( int n ) :
if n < 2 :
return n
else :
return fib_ rec _cy ( n - 1 ) + fib_ rec _cy ( n - 2)

_ _

In [ 55 ] : %tine fib rec cy ( 35 )
CPU tines : user 751 ns , sys : 4.37 ns , total : 756 ns

Производительность Python

345

Wall tine: 755 ns
Out[ 55 ]: 9227465
Основной недостаток рекурсивных алгоритмов заключается в том, что промежуточные результаты пересчитываются, а не кешируются. Решить такую
проблему позволяет декоратор, обеспечивающий кеширование промежуточных результатов, что позволяет увеличить скорость вычислений на два порядка.

_

In [56]: fron functools inport lru cache as cache
In [57]: @cache(naxsize= None) О
def fib. rec.py2(n):
if n < 2 :
return n
else:
return fib rec py2(n

_



_ _

_ _

-

_ _

1) + fib rec py2(n

- 2)

In [ 58 ] : %tine fib rec py2(35) ©
CPU tines: user 64 ps, sys: 28 ps, total: 92 ps
Wall tine: 98 ps
Out[58]: 9227465

_ _

In [ 59 ] : %tine fib rec py2(80) ©
CPU tines: user 38 ps, sys: 8 ps, total: 46 ps
Wall tine: 51 ps
Out[59]: 23416728348467685

О Кеширование промежуточных результатов...

© ...приводит к огромному ускорению вычислений.

Итеративный алгоритм
Несмотря на то что алгоритм вычисления п-то числа Фибоначчи можно реализовать рекурсивным способом, это вовсе не единственное решение. Ниже
представлена итеративная реализация, которая даже на чистом коде Python
работает быстрее, чем рекурсивная реализация с кешированием. Еще большего ускорения можно добиться благодаря пакету Numba, но самый быстрый
алгоритм реализуется с помощью пакета Cython.

_

In [60]: def fib it py(n):
x, у = 0 , 1
for i in range(:l , n + 1):
X, у = у , X + у
return x


346

Глава 10

__

In [61]: %time fib it py(80)
CPU times: user 19 ps, sys: le+03 ns, total: 20 ps
Wall time: 26 ps
Out[61]: 23416728348467685

_ _

In [62]: fib it nb

=

_

_

numba.jit(fib it_ py)

_

In [63]: %time fib it nb(80)
CPU times: user 57 ms, sys: 6.9 ms, total: 63.9 ms
Wall time: 62 ms
0ut[63]: 23416728348467685

_ _

In [64]: %time fib it nb(80)
CPU times: user 7 ps, sys: 1 ps, total: 8 ps
Wall time: 12.2 ps
0ut[64]: 23416728348467685

_

In [65]: %%cython
def fib it cyl(int n):
cdef long i
cdef long x = 0, у = 1
for i in range(l, n + 1):
x, у = у , x + у
return x

_

_

_

In [66]: %time fib it cyl(80)
CPU times: user 4 ps, sys: le+03 ns, total: 5 ps
Wall time: 11 ps
0ut[66]: 23416728348467685
Вы спросите, почему при такой скорости вычислений мы определяем только 80 - е число последовательности , а не, например , 150 - е? Оказывается , огра ничение связано с доступными типами данных. Как описывалось в главе 3,
Python способен работать с произвольно большими числами, но в случае ком пилируемых языков это не так. В частности , объект double позволяет хранить
вещественные числа длиной 64 бита. Для работы с еще большими числами в
пакете Cython предусмотрен специальный тип данных .

In [67]: %%time
fn = fib rec py2(150) О
print(fn) О
9969216677189303386214405760200

_ _

Производительность Python

347

CPU times: user 361 ps, sys: 115 ps, total: 476 ps
Wall time: 430 ps

_

In [68]: fn.bit length()
Out[68]: 103

©

__

In [69]: %%time
fn = fib it nb(150)
print(fn) ©

©

6792540214324356296
CPU times: user 270 ps, sys: 78 ps, total: 348 ps
Wall time: 297 ps

_

In [78]: fn.bit length()
Out[70]: 63

0

In [71]: %%time
fn = fib it cyl(150) ©
print(fn) ©
6792540214324356296
CPU times: user 255 ps, sys: 71 ps, total: 326 ps
Wall time: 279 ps

_ _

_

In [72]: fn.bit length()
Out[72]: 63

О

In [73]: %%cython
cdef extern from *:
ctypedef int intl28 1 intl28 t'
def fib it cу2(int n):
cdef intl28 i ©
cdef intl28 x = 0, у = 1 ©
for i in range(l, n + 1):
X, у = у , X + у
return x

_ __

_

©

In [74]: %%time
fn = fib it cy2(150) ©
print(fn) ©
9969216677189303386214405760200
CPU times: user 280 ps, sys: 115 ps, total: 395 ps
Wall time: 328 ps

_ _

348

Глава 10

_

In [ 75 ] : fn . bit length ( )
Out [ 75 ] : 103

©

О Чистый код Python работает достаточно быстро и возвращает корректный результат.

0 Битовая длина возвращаемого целочисленного значения равна 103 ( > 64).
© Версии Numba и Cython работают быстрее, но возвращают неправильные результаты.

О Это связано с проблемой переполнения, поскольку для объектов int максимальная длина



64 бита.

© Импорт специального 128-битного типа объектов int .
© Теперь функция fib_ i t _cy 2 ( ) , написанная на Cython, не только работает
быстрее, но и возвращает правильный результат.

Число 71



Последний из рассматриваемых в данном разделе алгоритмов
определение разрядов числа тс по методу Монте - Карло2. Метод основан на формуле
вычисления площади круга Л , имеющей вид А = лг 2 . Отсюда л -

—гЛ . У круга

с единичным радиусом л = А. Идея алгоритма заключается в моделировании
случайных точек с координатами ( x t y ) в пространстве х, у е [-1, 1] . Площадь
квадрата, в который вписан круг единичного радиуса, равна 4, а площадь са мого круга составляет дробную часть площади такого квадрата. Метод Мон те - Карло позволяет выразить эту дробь через соотношение количества случайных точек, попадающих внутрь круга и квадрата. Реализация алгоритма
приведена ниже ( рис. 10.1) .
In [ 76 ] : import r a n d o p )
import numpy as np
from pylab import mpl , p i t
pit . style . use ( 1 seaborn 1 )
mpl . rcParams [ ' font . family ' ]
%matplotlib inline

In [ 77 ] : rn

2

= ' serif '

= [ ( random . random ( ) * 2 for _ in range(500)]

1, random . randomQ * 2 - 1)

Примеры навеяны статьей на сайте Code Review Stack Exchange ( https : / / codereview .
stackexchange . com / questlons / 69370 / monte carlo pl calculation ).

-

- -

Производительность Python

349

In [78]: rn = np.array(rn)
rn[:5]
Out[78]: array([[ 0.45583018, 0.27676067],

[-0.70120038,
[ 0.07224045,
[-0.17450337,
[ 0.94896746,

-0.15196888],
0.90147321],

-0.47660912],
-0.31511879]])

In [79]: fig = pit.figure(figsize=(7, 7))
ax = ftg.add subplot(l, 1, 1)
circ = plt.Circle(
(0, 8), radius= l, edgecolor= ' g ',
lw=2.0, facecolor=' None') О
plt
Rectangle
box =
((- l, - 1), 2, 2, edgecolor= ' b 1 ,
.
alpha=8.3) ©
ax.add patch(circ) ©
ax.add patch(box) ©
plt.plot(rn[:, 0], rn[:, 1], ' r.') ©
plt.ylirn(- l.l, 1.1)
plt.xlin(~ l.1, 1.1)

_

_
_

О Рисуем круг единичного радиуса.
© Рисуем квадрат с длиной стороны, равной 2.

© Отображение случайных точек , имеющих нормальное распределение.
Применение пакета NumPy позволяет получить компактную реализацию
алгоритма, но требует больших ресурсов памяти. Общее время выполнения с
учетом параметризации составляет примерно одну секунду.

In [80]: n

=

int(le7)

In [81]: %tine rn = np.random.random((n, 2)) * 2 - 1
CPU tines: user 450 ns, sys: 87.9 ns, total: 538 ns
Wall tine: 573 ns
In [82]: rn.nbytes
Out[82]: 160000000
In [83]: %tine distance = np.sqrt((rn ** 2).sun(axis=l)) ©
distance[:8].round(3)
CPU tines: user 537 ns, sys: 198 ns, total: 736 ns
Wall tine: 651 ns

350

Глава 10

1.00





Ш

щ

••

Ч

*

0.75

%

*

0.50

••

9

•- •

%

••
••

и

••

•# ••
••

0.25

•и

г

••

••
0.00
%

-0.25

••

D

••

••

-0.50

••

••
••

v

-0.75

г


-1.00

%

••

%

вг

••

It

.
••



-1.00 -0.75 -0.50 -0.25

0.00

0.25

0.50

0.75

-



1.00

Рис. 10.1. Случайные точки с нормальным распределением, отложенные в
квадрате с длиной стороны 2, в который вписан круг единичного радиуса
0ut[83]: array([1.181, 1.061, 0.669, 1.206, 0.799, 0.579,
0.694, 0.941])

In [ 84 ] : %time frac = (distance y ( p ) :
М, I = р
dt = Т / М
S = np . zeros ( ( M + 1, I ) )
S[ 0 ] = so
rn = np random standard normal ( S shape ) ©
for t in r a n g e ( l , M + 1) : 0
for i in r a n g e ( I ) : 0
S [ t , i ] = S [ t - 1 , i ] * math . exp ( ( r - sigma **
2 / 2 ) * dt + sigma * math . sqrt ( dt ) * \
rn [ t 1] ) 0
return S

.

_

.

.

\

.

_

_

In [120 ] : % time S = mcs simulation py ( ( M , I ) )
CPU times : user 5.55 s , sys : 52.9 ms , total : 5.6 s
Wall time : 5.62 s
In [ 121] : S [ - l ] . mean ( ) ©
0 ut [121 ] : 38.22291254503985

In [ 122 ] : S0 * math . exp ( r * T )
0 ut [122 ] : 38.22611567563295

In [123 ] : К
In [ 124 ] : CO

©

= 40.0
=

math . exp ( - r * T ) * np . naxiPiurn ( K

-

S[ - l ] , 0 ) . nean ( )

©

In [125 ] : CO ©
0 ut [ 125 ] : 3.860545188088036

Производительность Python

361

О Количество интервалов дискретизации.

© Количество моделируемых траекторий.
© Случайные числа, векторизованно генерируемые за один проход.
© Вложенные циклы, в которых реализуется моделирование по методу
Эйлера.

© Средняя стоимость на конец временного периода.

© Теоретически ожидаемая стоимость на конец временного периода.
© Страйк -цена европейского пут-опциона.
© Оценка стоимости опциона по методу Монте- Карло.
На рис. 10.2 показана гистограмма полученных в процессе моделирования
значений на конец каждого временного периода ( срока исполнения европей ского пут -опциона ).
Среднее
Частота

5000

4000

3000

2000

1000

о
20

30

40

50

50

70

80

Рис. 10.2. Частотное распределение смоделированной стоимости опциона
на конец каждого периода

NumPy

_

_

NumPy- версия функции mcs simulation np ( ) не сильно отличается от базового варианта. В ней по- прежнему имеется один цикл Python, обеспечива 362

Глава 10

ющий пошаговую обработку временных интервалов. Второй цикл “ поглоща ется” векторизованным кодом, что позволяет ускорить вычисления в 20 раз.
In [ 127 ] : def mcs _ simulationjrp( p) :
М, I = р
dt = Т / М
S = np zeros (( M + 1, I) )
S[ 0 ] = SO
rn = np random standard_normal( S shape )
for t in range (l, M + 1) : О
S [ t ] = S [ t - 1] * np exp( ( r - sigma ** 2 / 2 )
dt + sigma * math sqrt ( dt ) * rn[ t ] )
return S

.

.

.

.

.

.

*
©

In [ 128 ] : %time S = mcs _ simulation_np ( ( M, I) )
CPU times: user 252 ms, sys : 32.9 ms, total: 285 ms

Wall time : 252 ms

.

In [ 129 ] : S [ -l] mean ( )
0ut [ 129 ] : 38.235136032258595

_

In [ 130 ] : %timeit S = mcs _ simulation np ( ( M, I) )
202 ms ± 27.7 ms per loop ( mean ± std dev
1 loop each)

.

. of

7 runs ,

О Пошаговая обработка временных интервалов.
@

Вычисления по методу Эйлера выполняются с помощью векторизованных инструментов NumPy, что позволяет обрабатывать все траектории
за один проход.

Numba
При реализации подобного рода алгоритмов имеет смысл применять па кет Numba, что позволяет добиться повышения производительности. Как
показано ниже, Numba - версия функции mcs _simulation_nb ( ) работает чуть
быстрее, чем NumPy- версия.

.

In [ 131] : mcs _ simulation_nb = numba jit ( mcs _ simulation_ py )

_

_

In [ 132 ] : %time S = mcs simulation nb ( ( M, I) ) ©
CPU times : user 673 ms, sys : 36.7 ms, total: 709 ms
Wall time: 764 ms

Производительность Python

363

_

_

In [133]: %time S = mcs simulation nb( ( M, I) ) ©
CPU times : user 239 ms, sys : 20.8 ms, total: 259 ms
Wall time : 265 ms

.

In [134]: S [ -l] mean( )
Out[134]: 38.22350694016539

.

In [135]: C0 = math exp( - r

*

T)

*

.

np maximum ( K

- S [ -l] ,

.

8 ) mean ( )

In [136]
: C0
Out [ 136 ] : 3.8303077438193833
In [137]
: %timeit S = mcs_ simulation_nb( ( M, I) ) ©
248 ms ± 20.6 ms per loop ( mean ± std dev
1 loop each )

.

.

of 7 runs,

О При первом вызове возникают накладные расходы, связанные с компиляцией.

© Второй вызов выполняется значительно быстрее.

Cython
При использовании пакета Cython необходимо вносить изменения в код.
Однако значительного ускорения вычислений здесь не происходит. Функция
mcs _ simulation cy ( ) работает даже чуть медленнее, чем версии NumPy и
Numba. Это связано с необходимостью преобразования результатов моделирования в объект ndarray, что отнимает время.

_

In [138]: %%cython
import numpy as np
cimport numpy as np
cimport cython
from libc.math cimport exp, sqrt
cdef float S0 = 36.
cdef float T = 1.0
cdef float г = 8.06
0.2
cdef float sigma
@cython boundscheck ( False)
@cython wraparound( False)

.

=

.

:y(p):
def mcsjsimulationj
cdef int M, I
M, I = P

364

Глава 10

cdef i n t t , i
cdef float dt = T / M
cdef doublet : , : ] S = np zeros ( ( M + 1, I) )
cdef doublet : > : ] rn = \
np random standard normal( ( M + 1, I) )
S [ 0 ] = SO
for t in range (l, M + 1) :
for i in range (I) :
S [ t , i] = S [ t -1, i] * \
exp ( ( r - sigma ** 2 / 2 ) * \
dt + sigma * sqrt ( dt ) * rn [ t , i] )

.

.

.

_

.

return np array ( S )
In [ 139 ] : %time S = mcs _ simulation_cy ( ( M, I) )
CPU times : user 237 ms, sys : 65.2 ms, total: 302 ms

Wall time : 271 ms

.

In [ 140 ] : S [ -l] mean ( )
Out [ 140 ] : 38.241735841791574

_

_

In [ 141] : %timeit S = mcs simulation cy ( ( M, I) )
221 ms ± 9.26 ms per loop ( mean ± std dev
1 loop each)

.

. of

7 runs,

Параллельные вычисления
Моделирование по методу Монте-Карло — это задача, которая хорошо
поддается параллельной обработке. Можно, например, распараллелить вычисление 100 000 траекторий на 10 процессов, каждый из которых будет
моделировать 10 000 траекторий. Другой вариант — моделировать 100 000
траекторий разными процессами, каждый из которых соответствует отдель ному финансовому инструменту. В этом разделе мы рассмотрим первый сце нарий.
Мы снова воспользуемся модулем multiprocessing. В следующем коде
общее количество траекторий I разбивается на несколько пакетов размером
Ир каждый, где р > 0. По завершении отдельных процессов результаты зано сятся в объект ndarray с помощью метода np hstackQ. Такой подход можно
применить к любой из рассмотренных выше реализаций алгоритма. В данном
случае выбранные параметры параллелизации не приводят к ускорению вы-

.

числений.

Производительность Python

365

In [ 142 ] : inport multiprocessing as mp

.

In [ 143 ] : pool = mp Pool( processes=4 )
In [ 144 ] : p = 20

О

©

.

.

_

_

In [ 145 ] : % timeit S = np hstack ( pool nap ( ncs sinulation np,
p * [ ( M, int (I / p ) ) ] ) )
288 ns ± 10.2 ns per loop ( nean ± std dev of 7 runs,
1 loop each )

.

.

.

_

.

In [ 146 ] : %tineit S = np hstack ( pool nap(ncs_ simulation nb,
p * [ ( M, int (I / p )) ] ) )
258 ns ± 8.69 ns per loop ( nean ± std dev of 7 runs,
1 loop each )

.

.

.

.

_

_

In [ 147 ] : %tineit S = np hstack ( pool nap( ncs sinulation cy,
p * [ ( M, int (I / p )) ] ) )
274 ns ± 11.9 ns per loop ( nean ± std dev of 7 runs,
1 loop each )

.

.

О Объект Pool, предназначенный для распараллеливания вычислений.
©

Количество пакетов, на которые разбивается блок моделируемых траек торий.

Стратегии параллельной обработки

(

к

В финансовых вычислениях используется много алгоритмов, допускающих параллельную обработку. В некоторых случаях существует даже несколько стратегий распараллеливания кода. Метод
Монте-Карло — прекрасный пример алгоритма, в котором можно
распределить моделирование между несколькими процессами или
же выполнять разные процессы моделирования на одном или не-

.

скольких компьютерах

Рекурсивный алгоритм библиотеки pandas
В этом разделе рассматривается важная тема, имеющая огромное значение
в финансовых приложениях: реализация рекурсивных функций для обработ ки финансовых временных рядов, хранящихся в объекте DataFrame библиотеки pandas. Несмотря на то что библиотека pandas позволяет выполнять

366

Глава 10

векторизованные операции с объектами Data Frame, некоторые алгоритмы
слишком сложно или даже невозможно векторизовать, и в результате финансовому аналитику приходится обрабатывать такие объекты в медленных
циклах Python. В следующем примере мы реализуем простой алгоритм вычисления экспоненциально взвешенного скользящего среднего (Exponentially
Weighted Moving Average EWMA ).
71}, вы Значение EWMA для финансового временного ряда St , t е {0,
числяется согласно уравнению 10.4.



Уравнение 10.4. Экспоненциально взвешенное скользящее среднее

= 50 ,
EWMAt = aSl + (\ - a ) EWMAl _ , te { l ..., Т } .
EWMAQ

l

Несмотря на простоту реализации полученный код может работать доста точно медленно.

Python
Вначале мы рассмотрим версию Python, в которой циклически обрабаты ваются значения Datetimelndex объекта DateFrame , хранящего временной
ряд отдельного финансового инструмента ( см. главу 8 ). На рис. 10.3 визуали зируется исходный временной ряд и временной ряд EWMA.
In [ 148 ] : import pandas as pd
In [149 ] : sym

=

In [150 ] : data

pd . DataFrame ( pd . read _csv ( \
/ source / tr eikon _eod _data . csv ' , index _col =8 ,
parse _dates = T rue ) [ sym ] ) . dropna ( )

' SPY '

=

_

\

In [151] : alpha = G . 2 S
In [ 152 ] : data [ ' EWMA ' ]

= data [ sym ] О

In [ 153] : %%time
for t in zip ( data . index , data . index[ l : ] ) :
data . loc[ t [ l ] , ' EWMA ' ] = ( alpha * data . loc[ t [ l ] , sym ] +
( 1 - alpha ) * data . loc[ t [ 0 ] , ' EWMA ' ] ) ©
CPU times : user 588 ms , sys : 16.4 ms , total : 605 ms
Wall time : 591 ms

\

In [ 154 ] : data . head ( )

Производительность Python

367

Out[154]:

SPY

EWMA

113.33
113.63
113.71
114.19

113.330000
113.405000
113.481250
113.658438
113.886328

Date
2010-01-04
2010-01-05
2010-01-06
2010-01-07
2010-01-08

114.57

In [155]: data[ data . index > ' 2017 - 1 - 1 ' ] . plot ( figsize = ( 10 , 6));

О Инициализация столбца EWMA.
© Реализация алгоритма с помощью цикла Python.
SPY
EWMA

280

270

260

250

240

230

&

г*4

,

Date

Рис. 10.3 . Финансовый временной ряд и экспоненциально взвешенное
скользящее среднее ( EWMA )

_

Теперь рассмотрим более общую функцию ewma py ( ) , которую можно
применить к отдельному столбцу или исходному временному ряду, представленному в виде объекта ndarray.

_

In [156]: def ewna py(x, alpha ) :
у = np . zeros like ( x )
y [8 ] = x [ 8 ]
for i in range(l, len ( x ) ) :
y [ i ] = alpha * x [ l ] + ( 1 - alpha ) * y [ i l ]
return у

_

-

368

Глава 10

_

In [ 157 ] : %tine data [ ' EWMA PY ' ] = ewna _ py ( data [ syrn ] , alpha ) О
CPU times : user 33.1 ms, sys : 1.22 ms , total: 34.3 ms
Wall time: 33.9 ms

_

.

In [ 158 ] : %time data [ ' EWMA_PY ' ] = ewma py ( data [ sym ] values, alpha )
CPU times : user 1.61 ms, sys : 44 ps, total: 1.65 ms

©

Wall time: 1.62 ms

О Применение функции непосредственно к объекту Series (т.е. к столбцу) .

© Применение функции к объекту ndarray, содержащему исходный временной ряд.

В результате мы получаем ускорение от 20 до более чем 100 раз.

Numba
Дальнейшего ускорения можно добиться с помощью пакета Numba, который весьма эффективен для такого типа алгоритмов. И действительно, если
применить функцию ewma _nb ( ) к объекту ndarray, то можно сократить время
вычислений еще на порядок.

_

.

In [ 159 ] : ewma nb = numba jit ( ewma _ py )
In [ 160 ] : %time data [ ' EWMA_NB ' ] = ewma _nb ( data [ sym ] , alpha ) ©
CPU times : user 269 ms, sys : 11.4 ms , total: 280 ms
Wall time : 294 ms

_

In [161 ] : %timeit data [ ' EWMA NB ' ] = ewma _ nb ( data [ sym ] , alpha ) ©
30.9 ms ± 1.21 ms per loop ( mean ± std dev of 7 runs,
10 loops each)

.

.

.

In [ 162 ] : % time data [ ' EWMA _NB ' ] =ewma _nb ( data [ sym ] values, alpha )
CPU times : user 94.1 ms, sys : 3.78 ms , total: 97.9 ms
Wall time: 97.6 ms

.

In [ 163 ] : %timeit data [ ' EWMA_NB ' ] = ewma _nb ( data [ sym ] values, alpha )
134 ps ± 12.5 ps per loop ( mean ± std dev of 7 runs ,
10000 loops each )

.

.

©

©

© Применение функции непосредственно к объекту Series ( т.е. к столбцу).
© Применение функции к объекту ndarray, содержащему исходный временной ряд.

Производительность Python

369

Cython
Функция ewma _cy ( ) , написанная на Cython, тоже позволяет существенно
ускорить вычисления, хотя и не настолько, как в предыдущем случае.
In [ 164 ] : %%cython
import numpy as np
cimport cython
@cython . boundscheck ( False )
@cython . wraparound(False)
def ewma . j:y ( doublet : ] x , float alpha ) :

cdef int i
cdef doublet : ] у = np . empty _ like ( x )
y[0 ] = X[ 0]
for i in range(l , len ( x ) ) :
y [ i ] = alpha * x [ i ] + ( 1 - alpha ) * y[ i

- 1]

return у

In [165 ] : %time data [ ' EWMA_ CY ' ] = ewma _cy ( data [ sym ] . values , alpha )
CPU times : user 2.98 ms , sys : 1.41 ms , total : 4.4 ms
Wall time : 5.96 ms

In [ 166 ] : % timeit data [ ' EWMA_ CY ' ] = ewma _cy ( data [ sym ] . values , alpha )
1.29 ms ± 194 ps per loop ( mean ± std . dev . of 7 runs ,
1000 loops each )

Этот пример наглядно показывает, что зачастую существует несколько вариантов реализации ( нестандартных) алгоритмов. Результаты будут одними и теми
же, но производительность решений оказывается разной. Время выполнения
кода в данном случае варьируется от 0,1 до 500 мс (разница более чем в 5000 раз).

я

370

Наилучший или первый из лучших
Как правило, алгоритмы достаточно легко транслируются в код
Python. Но вместе с тем можно легко получить такую реализацию
алгоритма, которая будет работать неприемлемо медленно в сравнении с другими возможными вариантами. Для задач интерактивного финансового анализа вполне достаточно выбрать первое из
лучших решений, т.е. такое, которое обеспечит ускорение вычислений, но не обязательно окажется самым производительным или
эффективным с точки зрения использования памяти. В те же время в коммерческих приложениях необходимо стремиться к получению наилучшего решения, пусть даже для этого придется проводить дополнительный анализ и формальное тестирование.

Глава 10

Резюме
Экосистема Python предлагает различные способы повышения производительности кода.

Идиомы и парадигмы
Очень часто при решении определенных задач отдельные идиомы и па радигмы Python приводят к получению более производительного кода.
В частности, парадигма векторизации вычислений не только позволяет
писать более компактный код, но и обеспечивает существенное ускорение вычислений (иногда за счет потребления дополнительной оператив ной памяти).

Программные пакеты
В Python имеется множество пакетов, предназначенных для решения
различных задач. Использование соответствующего пакета часто позво ляет получить более производительное решение. Хорошим примером
могут служить пакеты NumPy ( содержит класс ndarray) и pandas (со держит класс DataFrame).

Компиляция кода

Numba и Cython — мощные пакеты, позволяющие повышать произво дительность финансовых алгоритмов за счет динамической и статической компиляции кода Python.
Параллельные вычисления
Существуют специальные пакеты, в частности multiprocessing, позволяющие распараллелить выполнение кода Python. В этой главе мы
рассматривали примеры параллельной обработки на одном компьютере, но экосистема Python поддерживает и технологии кластерной параллелизации.
Основное преимущество рассмотренных в этой главе подходов к повышению производительности кода — простота реализации. Они не требуют
приложения больших усилий. Другими словами, благодаря имеющимся пакетам можно получить высокопроизводительный код без дополнительных

затрат.

Производительность Python

371

Дополнительные ресурсы
Для всех рассмотренных в этой главе пакетов имеется много полезных ре сурсов в Интернете.



Пакет Cython и соответствующий проект компилятора описаны на офи циальном сайте (http: / / cython . org );

• Документацию к модулю multiprocessing можно найти на сайте
https : / / docs . python . org / 3 / library / multiprocessing . html;
• Информацию о пакете Numba можно найти на сайтах http : // gi.thub .
com / numba / numba и https : / / numba . pydata . org.
Также рекомендуем следующие книги.

• Gorelick, Micha, and Ian Ozsvald. High Performance Python ( 2014, O’ Reilly).
• Smith, Kurt. Cython ( 2015, O’ Reilly).

372

Глава 10

ГЛАВА 11

Математические инструменты
Математики



это пастыри современного мира.

Билл Геде

С появлением профессиональных математиков на Уолл-стрит в 1980-х годах финансы превратились в дисциплину прикладной математики. Если ран ние исследовательские работы в области финансов содержали пространные
текстовые изложения и всего несколько математических формул, то современные труды финансовых аналитиков в основном состоят из математических выкладок с редкими вкраплениями текстовых комментариев.
В этой главе мы познакомимся с математическим аппаратом финансовых
расчетов, но не будем углубляться в чрезмерные детали. Для этого есть специ ализированная литература, мы же сконцентрируемся на инструментах Python.

Аппроксимация
В финансовых расчетах чаще всего применяются такие численные методы, как регрессия и интерполяция.
Выпуклое программирование
Задачи выпуклого программирования встречаются во многих финан совых дисциплинах ( например, в анализе деривативов при калибровке
модели ) .

Интегрирование
Оценка финансовых активов ( деривативов ) часто сводится к вычислению интегралов.
Символьные вычисления
SymPy это мощный пакет символьных вычислений, применяемый, в
частности, для решения ( систем) уравнений.



Аппроксимация
Начнем со стандартных операций импорта.
In [1] : import numpy as np
from pylab import pit , mpl

In [ 2 ] : pit . style . use ( ' seaborn ' )
mpl . rcParams [ 1 font . family ' ]
%matplotlib inline

= ' serif '

В этом разделе мы будет использовать следующую функцию, возвращающую результат математического выражения.
In [ 3 ] : def f ( х ) :
return np . sin ( x ) + 0 . 5 * x

Наша задача



аппроксимировать эту функцию в заданном интервале

регрессионными и интерполяционными методами. Но сначала необходимо по-

строить график функции, чтобы получить представление о том, каким дол жен быть результат аппроксимации. Мы будем работать в интервале [-2р, 2 р\ .
На рис. 11.1 показано, как выглядит график функции в интервале, который
задается с помощью функции np . linspace ( ) . Мы также создадим вспомога тельную функцию create _ plot ( ) , предназначенную для построения аналогичных графиков в последующих примерах.

_

In [ 4 ] : def create plot ( x , у , styles , labels , axlabels ) :
plt . figure ( figsize = ( 10 , 6 ) )
for i in r a n g e ( l e n ( x ) ) :
plt . plot ( x[ i ] , y [ i ] , styles[ i ] , label = labels [ i ] )
plt . xlabel ( axlabels [ 0 ] )
pit . ylabel ( axlabels [ 1 ] )
plt . legend ( loc = 0 )
In [ 5 ] : x

=

np . linspace ( - 2 * np . pi , 2 * np . pi , 50 )

О

_

In [ 6 ] : create plot ( [ x ] , [ f ( x ) ] , [ ' b ' ] , [ ' f ( x ) ' ] , [ ' x ' , ' f ( x ) ' ] )

О Значения x для построения графика и вычислений.

374

Глава 11

f ( x)

-4

-6

-2

0

2

4

6

Рис. 11.1. График тестовой функции

Регрессия



Регрессия эффективный метод аппроксимации, который хорошо подходит не только для линейных функций, но и для функций более высокого поряд ка. Численные методы, лежащие в основе регрессии, легко реализуются в коде
и обеспечивают высокую скорость вычислений. В общем случае регрессионная задача для набора так называемых базисных функций Ъ d е {1, ..., Z)},
*
*
заключается в поиске оптимальных параметров а, , ..., aD , уравнения 11.1,
где yi = / (х,. ) для i е {1, ..., / } наблюдений. Значения х. считаются независимыми наблюдениями, а у . зависимыми ( с функциональной или статисти ческой точки зрения ).



.

Уравнение 11.1 Регрессионная задача минимизации

.

,

min

а,

aD

1

I

/

/ 1

V

—I X=

у> -

D

\2

d 1

/

(*, )
ЕаА
=

Базисные функции, выраженные одночленами



представление базисных функций одночлена Простейший случай
ми: bx = 1, Ь2 = х, Ьъ = х2, Ь4 = х3 и т.д. В NumPy имеются соответствующие
функции, предназначенные как для нахождения оптимальных параметров
Математические инструменты

375

.

(np polyfitQ ), так и для аппроксимации заданного набора исходных значе ний ( np polyvalQ )
Параметры функции np polyfitQ описаны в табл. 11.1. Подставив в
функцию np polyval( p, х ) оптимальные регрессионные коэффициенты р,
возвращаемые функцией np polyfit ( ), можно получить регрессионные значения для координаты х .

.

.

.

.

.

Таблица ILL Параметры функции polyfit ( )
Параметр

Описание

х

Координата х ( значения независимой переменной)

у

Координата у (значения зависимой переменной)

deg

Степень регрессионного уравнения

full

Если равен Тгие, дополнительно возвращаются диагностические сведения

w

Веса, применяемые к координатам у

cov

Если равен True, дополнительно возвращается ковариационная матрица

Ниже показан типичный векторизованный способ записи линейного регрессионного уравнения (deg=l) с помощью функций np polyfit( ) и np polyval( ).
Получив массив регрессионных значений гу, мы можем сравнить результаты
регрессии с исходной функцией (рис. 11.2). Понятно, что линейная регрессия не
способна аппроксимировать тригонометрический член функции.

.

.

.

In [ 7 ] : res = np polyfit ( x, f ( x ) , deg=l, full=True )
In [ 8 ] : res ©
0u t [ 8 ] : ( a r r a y ( [ 4.28841952e - 01,
a r r a y ( [ 2 1 . 0 3 2 3 8 6 8 6 ] ),
2,
a r r a y ( [1 , 1 ] ) ,
1.1102230246251565 e - 14 )

.

О

- 1 . 3 1 4 9 9 9 5 0e - 1 6 ] ) ,

.

.

In [ 9 ] : r y = n p p o l y v a l ( r e s [ 0 ] , x ) ©
I n [ 1 0 ] : c r e a t e _ p l o t ( [ x, x ] , [ f ( x ) , r y ] , [ ' b \ ' г
[ ' f ( x ) \ ' Регрессия ' ] , [ V ,

. ' ],
' f ( x) ] )
1

О Линейная регрессия.

© Получение полной информации: параметров регрессии, остатков, ранга
матрицы, сингулярных значений и относительного числа обусловленности.
© Получение регрессионных значений с использованием регрессионных
параметров.
376

Глава 11



«X

Регрессия

2

1

х

=

г Г.

О

-1

-2

-3

и

-4

-2

О

2

ь

4

X

Рис. 11.2. Линейная регрессия

Чтобы аппроксимировать функцию sin ( ) , необходимо обратиться к регрессионным уравнениям более высокого порядка. В следующем примере используются базисные функции пятого порядка. Не удивительно, что регресси онная кривая теперь намного точнее повторяет исходную функцию ( рис. 11.3).
Тем не менее расхождения все еще достаточно значительны.
«X

Регрессия

2

1

X.


О

-1

-2

4

-6

-4

-2

О

2

4

I

.

X

Рис. 11.3. Регрессия с одночленами пятого порядка
Математические инструменты

377

In [ 11] : reg = np . p o l y f i t ( x , f ( x ) , deg = 5 )
ry = np . polyval ( reg , x )
In [ 12 ] : create _ plot ( [ x , x ] , [ f ( x ) , ry ] , [ ' b ' , ' г . ' ] ,
[ ' f ( x ) ' , ' Регрессия ' ] , [ V , ' f ( x ) ' ] )

В последнем варианте регрессии применяются одночлены 7- го порядка. Ре-

зультаты говорят сами за себя ( рис. 11.4 ).
In [13] : reg = np . p o l y f i t ( x , f ( x ) , 7 )
ry = np . polyval ( reg , x )
In [14 ] : np . allclose ( f ( x ) , ry )
Out[14 ] : False

О

In [ 15 ] : np . rnean ( ( f ( x ) - ry ) ** 2 )
Out[ IS ] : 0.0017769134759517689

©

In [ 16 ] : create _ plot ( [ x , x ] , [ f ( x ) , ry ] , [ ' b ' , ' r . ' ] ,
[ ' f ( x ) ' , ' Регрессия ' ] , [ ' x ' , ' f ( x ) ' ] )

О Проверка, являются ли значения функции и регрессионного уравнения
одинаковыми ( или, по крайней мере, близкими ).

© Вычисление среднеквадратической ошибки регрессионных значений для
соответствующих значений исходной функции.


f( x )
Регрессия

-6

-4

-2

0

2

4

Рис. 11.4. Регрессия с одночленами седьмого порядка
378

Глава 11

6

Отдельные базисные функции
Для получения более точных результатов регрессии следует грамотнее
подходить к выбору базисных функций, используя знание аппроксимируемой функции. В нашем случае отдельные базисные функции следует зада вать в виде матрицы ( т.е. с помощью объекта n d a r r a y библиотеки NumPy).
Вот как будет выглядеть такая матрица для уравнения с одночленами третьего порядка ( рис. 11.5). Регрессионное уравнение здесь вычисляется функцией
np . l i n a l g . l s t s q Q .


f( x )
Регрессия

\

/
/
/

/
/
/•

••
/

.. •

*

-6





••

••

••



••

••



•//



/

/

/

/



-4

-2

2

0

х

4

6

Рис . 11.5 . Регрессия с отдельными базисными функциями
In [17]: matrix

=

matrix [ 3 ,
matrix[ 2 ,
m a t r i x[ l ,
m a t r i x[ 0 ,
I n [ 1 8 ]: r e g

=

np . z e r o s ( ( 3 + 1 , l e n ( x ) ) )
: ] = x ** 3 ©
: ] = x ** 2 ©
:] = x ©

:] = i

=

©

n p . l i n a l g . l s t s q ( matrix . T , f ( x ) , rcond = N o n e ) [ 0 ]

I n [ 1 9 ] : r e g . round ( 4 )
0 u t [ 1 9 ]: a r r a y ( [ 0 .
I n [ 2 0 ]: r y

О

©
, 0.5628 ,

np . dot( reg , matrix )

- 0.

©

, - 0.0054 ] )

©

Математические инструменты

379

In [ 21 ] : create _ plot ( [ x , x ] , [ f ( x ) , ry ] , [ ' b ' , ' г . ' ] ,
[ ’ f ( x ) ' , ' Регрессия ' ] , [ ' x ' , ' f ( x ) ' ] )

О Объект nda г ray, представляющий матрицу значений для базисных функ ций.

© Базисные функции от свободного члена до члена третьего порядка.
© Расчет регрессионного уравнения.
О Оптимальные параметры регрессии.

© Регрессионная оценка значений функции.
Показанный на рис. 11.5 результат далек от ожидаемого. Традиционный
подход предполагает использование знаний об исходной функции. В частности, нам известно, что она содержит член s i n ( ) . Поэтому имеет смысл включить соответствующий элемент в набор базисных функций. Для простоты
заменим одночлен самого высокого порядка. В результате достигается идеальное совпадение ( рис. 11.6 ) .
In [ 22 ] : matrix[ 3 , : ]
In [ 23 ] : reg

=

=

np . sin ( x )

О

np . linalg . lstsq ( matrix . T , f ( x ) , rcond = None ) [ 0 ]

In [ 24 ] : reg . round ( 4 ) ©
0ut [ 24 ] : array ( [ 0 . , 0.5 , 0 . , 1 . ] )
In [ 25 ] : ry

=

np . dot ( reg , matrix )

In [ 26 ] : np . allclose ( f ( x ) , ry )
Out [ 26 ] : True

©

In [ 27 ] : np . mean ( ( f ( x ) - ry ) ** 2 )
Out [ 27 ] : 3.404735992885531e - 31

©

_

In [ 28 ] : create plot ( [ x , x ] , [ f ( x ) , ry ] , [ ' b ' , ' r . ' ] ,
[ ' f ( x ) ' , ' Регрессия ' ] , [ ' x ' , ' f ( x ) ' ] )

О Новая базисная функция, описывающая известное поведение исходной
функции.

©

Оптимальные параметры регрессии.

© Точность регрессии оказывается идеальной.
380

Глава 11



f ( x)
Регрессия

-6

-4

-2

0

4

2

6

х

Рис. 11.6. Регрессия с базисной функцией синуса

Зашумленные данные
Регрессионные методы прекрасно работают даже с зашумленными данными, полученными в результате некорректного моделирования или (неточных)
измерений В качестве иллюстрации сгенерируем независимые и зависимые
наблюдения, включающие шум. Как видно на рис. 11.7, результаты регрессии
оказались близки к исходной функции В определенном смысле регрессия
усредняет шум до какого -то уровня.

.

.

.

.

.

In [ 29 ] : xn = np linspace ( ~ 2 * np pl, 2 * np pi, 50 ) О
хп = хп + 0.15 * np random standard_nomal( len( xn ) ) ©
yn = f ( xn) + 0.25 * np random standard_normal ( len ( xn) )

.

.

.

.

©

.

In [ 30 ] : reg = np polyfit ( xn, yn, 7 )
ry = np polyval ( reg, xn)

.

_

. ' ],

In [ 31] : create plot ( [ x, x ] , [ f ( x ) , r y ] , [ ' b ' , ' r
[ ' f ( x ) e , ' Р е г р е с с и я ' ], [ V ,

' f ( x) ' ])

О Новые детерминированные значения х.

© Добавление шума к значениям х.
© Добавление шума к значениям у .
Математические инструменты

381

f( x )





Регрессия



•/



Ж

S•

••

JL



Ж

/•
F



-6

-4

-2

2

0

х

4

6

Рис. 11.7 . Регрессия с зашумленными данными

Неотсортированные данные
Еще один важный аспект регрессии состоит в том, что она позволяет ра ботать с неотсортированными данными. В предыдущих примерах мы имели
дело с отсортированными значениями х, что вовсе не обязательно. Попробуем
убедиться в этом, рандомизировав значения х. В следующем примере исход ные данные никак не упорядочены.
I n [ 3 2 ] : xu
yu

=

=

.

n p random . r a n d ( 5 8 ) * 4 * n p . p i
f ( xu )

-

2 * np. p i

.
.

О

I n [ 3 3 ] : p r i n t ( xu [ :1 0 ] round ( 2 ) )
p r i n t( yu[ : 10 ] round ( 2 ) )
[ - 4 . 1 7 - 0 . 1 1 1 . 9 1 2 . 3 3 3 . 3 4 - 0 . 9 6 5 . 8 1 4 . 9 2 - 4 . 5 6 - 5 . 4 2]
[ - 1 . 2 3 - 0 . 1 7 - 1 . 9 1 . 8 9 1 . 4 7 1 . 2 9 2 . 4 5 1 . 4 8 1 . 2 9 - 1 . 9 5]

-

-

-

I n [ 3 4 ] : r e g = n p . p o l y f i t ( xu , y u , 5 )
r y = n p . p o l y v a l ( r e g , xu )

_

I n [ 3 5 ] : c r e a t e p l o t ( [ xu , xu ] , [ yu , r y ] , [ 1 b . ' , ' го ' ] ,
[ ' f ( x ) ' , ' Регрессия ' ] , [ V , ' f ( x ) ' ] )

О Рандомизация значений x.

382

Глава 11

Как и в случае с зашумленными данными, регрессионный подход не учи тывает порядок наблюдений. Это изначально следует из описания задачи ми нимизации ( см. уравнение 11.1) и также иллюстрируется результатами, пред ставленными на рис. 11.8.


3



f (x )
Регрессия

2

г

•• •
«

••

’#

1

О

t

#

-1

t*

9 Фф
ЛФ

Ф

-2

+

?*

*
-6

-4

-2

О

2

х

4

6

Рис. 11.8. Регрессия неотсортированных данных

Регрессия многомерных зависимостей
Еще одно удобство регрессионного подхода по методу наименьших квадра тов заключается в том, что он легко переносится на многомерные зависимости, не требуя существенных модификаций. В качестве примера рассмотрим
следующую функцию fm ( ) .
In [ 36 ] : def frn ( p ) :
x, у = p
return np sin ( x ) + 0.25 * x + np . sqrt ( y ) + 0.65 * у ** 2

.

Чтобы корректно визуализировать эту функцию, мы должны оперировать
двухмерными сетками независимых наблюдений. График функции f m ( ) в
трехмерном координатном пространстве XYZ показан на рис. 11.9.
In [ 37 ] : х = np . linspace ( 0 , 10 , 20 )
у = np . linspace( 0 , 10 , 20 )
X , Y = np meshgrid ( x , у ) О

.

Математические инструменты

383

In [ 38 ] : Z

= fm ( ( X , Y ) )
x = X . flatten ( )
у = Y . flattenQ

©
©

©

In [ 39 ] : from mpl toolkits . mplot 3d import Axes3 D
^

In [ 48 ] : fig

= pit . figure ( figsize= ( 12 , 8 ) )
ax = fig . gca ( projection = ' 3d ' )
surf = ax . plot _ surface ( X , Y , Z , rstride = 2 , cstride = 2 ,
cmap = ' coolwarm ' , linewidth = Q . 5 ,
antialiased = True )
ax . set xlabel ( ' x ' )
ax set ylabel ( у ' )
ax . set zlabel ( ' f ( x , y ) ' )
fig colorbar ( surf , shrink =8.5, aspect = 5 )

.

.

__
_

1

О Генерирование двухмерных объектов ndarray ( “ сеток ” ) из одномерных
объектов ndarray.

© Получение одномерных объектов nda г ray из двухмерных объектов nda г гау.
© Импорт инструментов ЗБ-визуализации из библиотеки matplotlib.

ю
10
8
8

4

6

4
О

10
8

6

О

2
4
X

6
8

10

О

Рис . 11.9 . График функции двух переменных
384

Глава 11

Чтобы получить хорошие результаты регрессии, нужно правильно подобрать базисные функции. Зная структуру функции fm ( ) , мы выбираем в качестве базисных функции np sin ( ) и np . sqrt ( ) . Результаты регрессии пред ставлены на рис. 11.10.

.

.

I n [ 41] : m a t r i x = np zeros ( ( i e n ( x ) , б + 1) )
m a t r i x [ : , 6 ] = np . s q r t ( y ) О
m a t r i x [ : , 5 ] = np s i n ( x ) ©
m a t r i x [ : , 4 ] = у ** 2
matrixf : , 3 ] = x ** 2
matrix [ : , 2 ] = у
matrix [ : , 1] = x
matrix [ : , 0 ] = 1

.

=

In [ 42 ] : reg

In [ 43 ] : RZ
In [ 44 ] : fig

.

np . linalg lstsq ( matrix , fm ( ( x , y ) ) , rcond = None ) [ 0 ]

.

.

np dot ( matrix , reg ) reshape ( ( 20 , 20 ) )

=
=

.

©

plt figure ( figsize ( 12 , 8 ) )

=

s

ax = fig . gca ( projection = ' 3d ' )
surfl = ax . plot surface ( X , Y , Z , rstride = 2 , cstride = 2 ,
cmap = mpl . cm . coolwarm , linewidth =0 , 5 ,
antialiased = True ) 0
surf 2 = ax . plot wireframe ( X , Y , RZ , rstride = 2 ,
cstride = 2 , label = ' Регрессия ' ) ©
(
)
'
'
.
set
xlabel
ax
x
ax . set ylabel ( ' у ' )
ax . set zlabel ( ' f ( x , y ) ' )
ax . legend ( )
fig . colorbar ( surf , shrink =0.5 , aspect = 5 )

_
_

_

__

О Функция np . sqrt ( ) для переменной у.

0 Функция np . sin ( ) для переменной х.
0 Преобразование результатов регрессии в двухмерную структуру ( сетку).
0 Построение поверхности, представляющей исходную функцию.
0 Построение регрессионной поверхности.

Математические инструменты

385

Регрессия

10

L0

*

<

6

4
2

"

10

О

8

N

X


_

И

4

2

с
И

о
4

2

У

1
X

2

6
10

О

.

Рис 11.10. Регрессионная поверхность для функции двух переменных

(

Регрессия
Регрессия по методу наименьших квадратов имеет несколько об ластей применения, включая аппроксимацию простых функций, а
{ также аппроксимацию функций на основе зашумленных или неотсортированных данных. Такой подход можно применять как к
одномерным, так и к многомерным задачам. Математический ап парат при этом остается практически неизменным.

Интерполяция
По сравнению с регрессией интерполяция (например, кубическими сплайнами ) требует более сложных математических расчетов. Она также ограни чена лишь задачами с малым числом размерностей. Для набора упорядоченных ( по координате X) наблюдений интерполяция предполагает такую
регрессию между двумя соседними точками, чтобы точки данных не только
идеально соответствовали результирующей кусочно - определенной интерполяционной функции, но и обеспечивали непрерывную дифференцируемость такой функции в рассматриваемом диапазоне значений. Чтобы получить непрерывно дифференцируемую функцию, интерполяцию нужно

386

Глава 11

выполнять с помощью многочленов не менее чем третьего порядка, т.е. с
помощью кубических сплайнов Тем не менее в большинстве случаев хоро ший результат можно получить при интерполяции квадратичными и даже
линейными сплайнами.
В следующем примере выполняется линейная интерполяция (рис. 11.11)

.

.





f( x )
Интерполяция

-6

-4

-2

0

2

4

6

х

.

Рис 11.11. Интерполяция линейными сплайнами ( полный набор значений )

.

In [ 45 ] : import scipy interpolate as spi
In [ 46 ] : x = np . linspace ( - 2

О

* np . pi , 2 * np . pi , 25 )

In [ 47 ] : def f ( x ) :
return np . sin ( x ) + 0.5

* x

In [ 48 ] : ipo = spi . splrep ( x , f ( x ) , k=l)
In [ 49 ] : iy = spi . splev ( x , ipo )
In [ 50 ] : np . allclose ( f ( x ) , iy )
Out [ 50 ] : True

©

©
©

In [ 51 ] : create_plot ( [ x , x ] , [ f ( x ) , iy ] , [ ’ b ' , ' r o ' ] ,
[ ' f ( x ) ' , ' Интерполяция ' ] , [ ' x ' , ' f ( x ) ' ] )

Математические инструменты

387

О Импорт необходимого модуля из пакета SciPy.

© Интерполяция линейными сплайнами.
© Получение интерполированных значений.
© Проверка близости интерполированных значений к соответствующим
значениям функции.

Учитывая упорядоченность значений х , практическая реализация рассмотренного выше метода оказалась столь же простой, как и в случае использования функций np . poly fit ( ) и np . polyval ( ) . Все необходимые вычисления
выполняются функциями sci . splrepO и sci . splevQ . В табл. 11.2 приведено описание основных параметров функции sci . splrepO .
Таблица 11.2. Параметры функции sci . splrep( )
Параметр

Описание

х

Упорядоченные значения координаты х ( независимая переменная )

У

Значения координаты у ( зависимая переменная ) для упорядоченных значений х

w

Веса для координату

xb , хе

Шаг интерполяции ; в случае None принимается равным [ х [ 0 ] , х [ -1]]

к

Порядок сплайна ( 1 <

S

к aqa + bqb ,

а, b > 0.
Подставив в эту систему все известные числовые значения, ее можно при вести к следующему виду (обратите внимание на переход к задаче минимизации отрицательной ожидаемой полезности ).

Уравнение 11.3. Задача максимизации ожидаемой полезности ( 2 )

^ ^

(

/ ),

min - E ( M ( W1 ) ) = - 0, 5

+ 0, 5a

= а - 15 + 6 - 5,
wld = ci ‘ 5 + b - l 2 ,
w] u

-

100 > а 10 + 6 10,
a, й > 0.
*

В решении задачи нам поможет функция scipy . optimize . minimize ( ).
В качестве аргументов она получает не только оптимизируемую функцию, но
и условия в виде равенств и неравенств, ограничивающих оптимизацию ( спи сок словарей ) , а также граничные значения параметров ( кортеж кортежей)1.
Ниже показано, как перевести уравнение 11.3 в код Python.
In [ 71] : import math

In [ 72 ] : def Eu ( p ) : О
s, b = p
return - ( 0.5 * math sqrt ( s * 15 + b * 5 ) +
0.5 * math . sqrt ( s * 5 + b * 12 ) )

.

In [ 73 ] : cons

In [ 74 ] : bnds

‘ Подробное
_

=
=

( ( 0 , 1060 ) , ( 0 , 1000 ) )

-

p [ 0 ] * 10

-

p[1] * 10})

©

©

описание функции minimize ( ) приведено в документации (http : / / bit . ly /

using minimize ).

396

({ ' type ' : ' ineq ' ,
' f u n ' : lambda p : 100

Глава 11

In [ 75 ] : result = sco . minimize ( Eu , [ 5, 5 ] , method= ' SLSQP ' ,

bounds =bnds , constralnts=cons )

О

О Функция, минимизируемая в задаче максимизации ожидаемой полезности.

© Ограничение в виде неравенства, заданное с помощью объекта diet.
© Граничные значения параметров (выбираемые в широком диапазоне).
0 Условная оптимизация.
Возвращаемый объект содержит всю необходимую информацию. Что касается минимального значения функции, то нужно помнить о необходимости
замены знака на противоположный.
In [ 76 ] : result
fun :
0ut [ 76 ] :
jac :
message :
nfev :
nit :
njev :
status :
success :
x:

- 9.700883611487832
array ( [ - 0.48508096 , - 0.48489535 ] )
' Optimization terminated successfully . '
21
5
5
0
True
array ( [ 8.02547122 , 1.97452878 ] )

In [ 77 ] : resultfx ' ] О
0ut [ 77 ] : array ( [ 8.02547122 , 1.97452878 ] )
In [ 78 ] : - result [ ' fun ' ] ©
0ut [ 78 ] : 9.700883611487832
In [ 79 ] : np . dot ( result [ ' x ' ] , [ 10, 10 ] )
0ut [ 79 ] : 99.99999999999999

©

О Оптимальные значения переменных (т.е. оптимальный портфель).

© Отрицательное минимальное значение функции как решение задачи оптимизации.

© Достигнут лимит бюджета; инвестируются все средства.

Математические инструменты

397

Интегрирование
К интегрированию часто прибегают в задачах оценки опционов. Это связа но с тем, что в общем случае риск- нейтральную стоимость деривативов можно выразить как дисконтированное ожидание доходности в риск- нейтральных или мартингальных условиях. В свою очередь, ожидание вычисляется
как сумма ( дискретная функция ) или интеграл ( непрерывная функция ). Все
необходимые нам функции численного интегрирования содержатся в модуле scipy integrate. Функция, на примере которой мы будем знакомиться с
методами численного интегрирования в Python, взята из раздела “ Аппрокси -

.

мация”.

.

In [ 80 ] : import scipy integrate as

set

In [ 81] : def f ( x ) :
return np . sin ( x ) + 0 , 5 * x

Интервал интегрирования задается равным [ 0,5; 9,5]; в результате получа ем определенный интеграл, описываемый уравнением 11.4.

Уравнение 11.4 . Интеграл тестовой функции

Ниже объявляются объекты Python, требуемые для вычисления такого ин -

теграла.

In [ 82 ] : х
У

= np . linspace ( 0 ,
= f (x)
а = 0.5 О
b = 9.5 ©
lx


10 )

= np . linspace ( a , b ) ©

=

f ( 1х )

0

О Нижний предел интегрирования.

© Верхний предел интегрирования.
© Значения интервала интегрирования.

0 Значения подынтегральной функции.

398

Глава 11

Результат интегрирования представляется областью, выделенной серым
цветом под графиком интегрируемой функции (рис. 11.15 ) 2.

.

I n [ 83 ] : from matplotlib patches import Polygon

.

I n [ 84 ] : f i g, ax = p l t subplots ( figsize= ( 10 , 6 ) )
p l t plot ( x , y, ' b ' , linewidth= 2 )
p i t , ylim( bottom=0 )
l x = np linspace ( a, b )
Iy = f(lx)
verts = [ ( a, 0 ) ] + l i s t ( zip ( I x, I y ) ) + [ ( b, 0 ) ]
poly = Polygon( verts , facecolor = ' 0.7 ' , edgecolor = ' 0.5 ' )
ax add patch( poly )
p l t text ( 0.75 * ( a + b ) , 1.5, r » $ \ i n t _ aAb f ( x ) dx $ »,
horizontalalignment = ' center ' , fontsize= 20 )
p l t figtext ( 0.9 , 0.075, $ x $ ' )
p l t figtext ( 0.075, 0.9, ’ $ f ( x ) $ ’ )
ax set xticks ( ( a, b ) )
ax set xticklabels ( ( ' $ a $ ' , ' $ b $ ' ) )
ax set yticks ( [ f ( a ) , f ( b ) ] ) ;

.

.

. _
.
.
.

1

. _
. _
. _

fix )

4.675

J

a

f ( x )d x

0.729

b

x

.

Puc 11.15. Результат интегрирования как площадь области

под графиком функции

2

О том, как строить такие графики, см. в главе 7.

Математические инструменты

399

Численное интегрирование
Модуль scipy . integrate содержит ряд функций, предназначенных для
численного интегрирования различных математических функций в заданных
нижнем и верхнем пределах. В качестве примеров можно привести функции
sci . fixed quad ( ) ( метод Гаусса) , sci . quadQ ( адаптивная квадратура ) и
sci . rombergQ ( метод Ромберга ).

_

In [85 ] : sci.fixed _ quad(f, а, b)[ 0 ]
Out [ 85 ]: 24.366995967084602
In [ 86 ] : sci.quad(f, a, b)[ 0 ]
Out [ 86 ]: 24.374754718086752

In [ 87 ] : set.romberg(f, a , b)
Out[ 87 ]: 24.374754718086713

Есть также ряд функций интегрирования, которым в качестве аргументов
можно передавать объекты list ( значения функции ) или ndarray ( значения
интервала интегрирования ). Например, функция sci . trapzQ выполняет
по методу
интегрирование по методу трапеций, а функция set . simps ( )
Симпсона.



In [ 88 ] : xi = np.linspace(0.5 , 9 , 5 , 25 )
In [ 89 ] : sci.trapz(f(xi), xi)
0ut[89]: 24.352733271544516
In [ 90 ] : sci.simps(f(xi), xi)
Out [ 90 ]: 24.37496418455075

Интегрирование методами моделирования
Оценка опционов и деривативов по методу Монте- Карло ( рассматривается в главе 12) основана на предположении о возможности интегрирования
моделируемой функции. Для этого следует выбрать I случайных значений х в
интервале интегрирования и вычислить значение подынтегральной функции
для каждого из них. Далее значения функции суммируются, и вычисляется
среднее по интервалу. Умножив полученное среднее на длину интервала, на ходим оценочное значение интеграла .
Следующие результаты показывают, что оценка интеграла, вычисляемая
по методу Монте- Карло, сходится, хоть и не монотонно, к действительному

400

Глава 11

значению интеграла при увеличении количества случайных выборок. Оцен ка получается достаточно близкой даже для относительно небольшого числа
случайных выборок.
In [ 91] : for i in range ( l , 20 ) :
np.random.seed(1008)
x = np.random.random(i * 10 ) * ( b - a)
print(np.mean(f(x)) * ( b - a))

+

a

О

24.804762279331463
26.522918898332378
26.265547519223976
26.02770339943824
24.99954181440844
23.881810141621663
23.527912274843253
23.507857658961207
23.67236746066989
23.679410416062886
24.424401707879305
24.239005346819056
24.115396924962802
24.424191987566726
23.924933080533783
24.19484212027875
24.117348378249833
24.100690929662274
23.76905109847816

О Количество случайных значений х увеличивается на каждой итерации.

Символьные вычисления
В предыдущих разделах мы имели дело преимущественно с численными
методами. В этом разделе вам предстоит познакомиться с символьными вычислениями, которые находят широкое применение в финансовой математике.
Инструменты символьных вычислений содержатся в библиотеке SymPy.

Общие сведения
Библиотека SymPy предоставляет собственные классы объектов. Базовый
класс называется Symbol.

Математические инструменты

401

I n [92]: i m p o r t sympy a s sy
I n [93]: x
у

=
=

s y . Symbol( ' x 1 )
sy . Symbol( ' у ' )

О
О

I n [94]: type(x)
O u t[ 9 4 ] : s y m p y . c o r e . s y m b o l . S y m b o l
I n [ 9 5]: s y . s q r t ( x )
0u t [ 9 5 ] : s q r t ( x )

©

I n [ 9 6 ] : 3 + s y . s q r t ( x ) - 4 ** 2
0u t[ 9 6 ] : s q r t ( x ) - 1 3
I n [ 9 7 ]: f

=

©

x ** 2 + 3 + 0 . 5 * x ** 2 + 3

I n [ 9 8 ]: s y . s i m p l i f y ( f )
0u t [ 9 8 ] : 1.5*x** 2 + 4.5

/

2

©

©

О Определение символов, используемых в вычислениях.

© Применение функции к символу.
© Численное выражение с использованием символа.

© Символьное представление функции.
© Упрощенная запись функции.
Такой подход заметно отличается от того, к чему мы привыкли. Несмотря
на то что у х нет числового значения, функция вычисления квадратного корня х все равно определяется с помощью библиотеки SymPy, поскольку х
это объект Symbol. В результате вызов sy . sqrt ( x ) может быть частью произвольного математического выражения. Заметьте, что SymPy автоматически
упрощает математические выражения. С помощью объектов Symbol можно
определять произвольные функции, которые не следует путать с функциями



Python.
В SymPy поддерживаются три способа кодировки математических выраже ний:

• LaTeX;
• Unicode;
• ASCII.
402

Глава 11

При работе исключительно в среде Jupyter Notebook ( на HTML-странице)
предпочтителен формат LaTeX как наиболее визуально привлекательный. В
ASCII, чтобы
следующем примере используется самый простой вариант
подчеркнуть отсутствие ручного форматирования.



_

_

In [ 99 ] : sy . init printing ( pretty _ print = False , use unicode = False )
In [ 100 ] : print ( sy . pretty ( f ) )
2
1.5*x + 4.5
In [ 101 ] : print ( sy . pretty ( sy . sqrt ( x ) + 0.5 ) )

\/ x

+ 0.5

Библиотека SymPy включает большое количество других полезных матема тических функций, например для представления числа р. В следующем примере показаны первые и последние 40 знаков после запятой в строковом пред ставлении числа р ( доходит до 400 000- го разряда ). Также демонстрируется,
как найти в записи числа р шестизначную дату рождения в формате “ ддммгг”.

_

In [102 ] : %tine pi str = str ( sy . N ( sy . pi , 400000 ) ) О
CPU tines : user 400 ns , sys : 10.9 ns , total : 411 ns
Wall tine : 501 ns
In [ 103] : pi _str [ : 42 ] ©
Out [ 103 ] : ' 3.1415926535897932384626433832795028841971 '

_

In [ 104 ] : pi str [ - 40 : ] ©
Out [ 104 ] : ' 8245672736856312185020980470362464176198 *
In [ 105 ] : % tine pi _ str . find ( ' 061072 ' ) 0
CPU tines : user 115 ps , sys : le + 03 ns , total : 116 ps
Wall tine : 120 ps
Out [ 105 ] : 80847

О Получение строкового представления первых 400 000 разрядов числа р.

© Отображение первых 40 цифр после запятой...
© ...и 40 последних цифр.
О Поиск шестизначной даты рождения в строке.
Математические инструменты

403

Решение уравнений



возможность решения уравОдно из достоинств библиотеки SymPy
нений, таких, например, как х 2 - 1 = 0 . Предполагается, что ищется корень
уравнения, в котором заданное выражение равно 0. Следовательно, уравнения вида х 2 - 1 = 3 необходимо предварительно перестроить. Библиотеке по силам справиться и с решением более сложных уравнений, таких как
х3 + 0, 5х 2 - 1 = 0 . Наконец, она способна находить комплексные корни в случае уравнений вида х 2 + у — 0 .
In [ 186 ] : sy.solve( x ** 2 - 1)
Out [ 106 ] : [ -1, 1]
In [107 ] : sy.solve( x ** 2
Out [187 ] : [ - 2, 2]

-

1

-

3)

-

In [ 108 ] : sy.solve(x ** 3 + 8.5 * x * t 2 1)
Out [ 188 ]: [0.858094329496553, - 0.679047164748276 - 0.839206763026694*1,
- 0.679047164748276 + 0.839206763026694*1]

In [109 ] : sy.solve(x ** 2 + у ** 2. )
Out [109 ]: [{x: - I*y}, {x: I*y}]

Интегрирование



поддержка интегрирования
Еще одно достоинство библиотеки SymPy
и дифференцирования. В следующем примере выполняется численное и символьное интегрирование уже знакомой нам тестовой функции. Предварительно необходимо задать пределы интегрирования в виде объектов Symbol.
In [ 110 ] : a, b = sy.symbols( ' a b')

О

In [ 111] : I = sy.Integral(sy.sin(x)

+

In [112 ] : print(sy.pretty(I))
b

©

/

| (0.5*x

/

404

Глава 11

+

sin(x)) dx

0.5 * x, (x, a, b))

©

_

=

In [113 ] : int func

sy . integrate ( sy . sin ( x ) + 0.5 * x , x )

_

.

In [ 114 ] : print ( sy pretty ( int func ) )

©

©

2
0.25*x

=
=

In [115 ] : Fb
Fa

-

cos ( x )

__

.
.

int func subs ( x , 9.5 ) . evalf ( )
int func subs ( x , 0.5 ) . evalf ( )

©
©

In [ 116 ] : Fb - Fa ©
Out [ 116 ] : 24.3747547180867

О Объекты Symbol, задающие пределы интегрирования.

© Определение и вывод объекта Integral.
© Нахождение и вывод первообразной.

0 Значения первообразных в пределах интегрирования, вычисляемые с помощью методов subs ( ) и evalf ( ) .

© Точное числовое значение интеграла.
Интеграл можно также вычислить символьно в заданных пределах ин тегрирования.

_

_

=

In [ 117 ] : int func limits

sy . lntegrate ( sy . sin ( x ) + 0 . 5 * x ,
( x, a , b) ) ©

_

.

_

In [ 118 ] : print ( sy pretty ( int func limits ) ) ©
2
2
- 0.25*a + 0.25* b + cos ( a ) - cos ( b )

_

_

.

.

In [ 119 ]: int func limits subs ({a : 0.5 , b : 9 , 5}) evalf ( )
0 ut [ 119 ] : 24.3747547180868

.

.

In [ 1 2 0 ] : sy integrate ( sy sin ( x ) + 0 , 5 * x , ( x , 0 , 5 , 9.5 ) )
Out [ 120 ] : 24.3747547180867

©
©

О Символьное интегрирование.

© Численное интегрирование с подстановкой объекта diet .
© Численное интегрирование в один проход.

Математические инструменты

405

Дифференцирование
Производная первообразной равна исходной функции. Проверим это, при менив функцию sy . diff ( ) к первообразной, записанной в символьном виде.

_

In [ 121] : int func . diff ( )
Out [ 121 ] : 0.5*х + sin ( x )

Как и в примере с интегрированием, мы воспользуемся дифференцированием для поиска точного решения рассмотренной ранее задачи выпуклого программирования . Для этого мы запишем соответствующую функцию
в символьном виде, определим ее частные производные и затем найдем
корни.
Необходимым, но не достаточным условием глобального минимума функции является равенство ее частных производных нулю. При этом нет никакой
гарантии того, что символьное решение будет найдено. Следует учитывать
как алгоритмические трудности, так и возможность существования нескольких решений. Но можно решить два уравнения первого порядка в численном
виде, предоставив “ обоснованные” догадки, которые были получены при решении задач глобальной и локальной минимизации.
In [ 122 ] : f

=

( sy . sin ( x ) + 0.05 * х ** 2
+ sy . sin ( y ) + 0 , 05 * у ** 2 )

_ = sy . diff ( f , x )
del _x ©
0 ut [ 123] : 0.1*x + cos ( x )
In [ 123] : del x

_

In [124 ] : del y = sy . diff ( f , y )
del_y ©
Out [ 124 ] : 0.1* y + cos ( y )

©

©

In [125 ] : xo

= sy . nsolve ( del_x , - 1.5 )
xo ©
Out [125 ] : - 1.42755177876459

©

In [ 126 ] : yo = sy . nsolve ( del_y , - 1.5 )
yo ©
Out [126 ] : - 1.42755177876459

©

In [127 ] : f . subs ( { x : xo , у : yo} ) . evalf ( )
Out [127 ] : - 1.77572565314742

406

Глава 11

О

©

О Символьная запись функции.
Q

Получение и вывод двух частных производных.

@

Обоснованные догадки о существовании корней и полученные опти мальные значения.

0 Значение глобального минимума функции.
Опять-таки, необоснованные/ произвольные предположения могут привести к нахождению локального, а не глобального минимума.
In [ 128 ] : хо

хо

=

_

О

_

О

sy . nsolve ( del x , 1.5 )

Out [ 128 ] : 1.74632928225285

.

In [ 129 ] : уо = sy nsolve ( del y , 1.5 )
уо
Out [ 129 ] : 1.74632928225285

In [ 130 ] : f . subs ({x : хо , у : yo}) . evalf ( )
0 ut [ 13 Q ] : 2.27423381055640

©

О Необоснованные предположения о существовании корней.
Q

Значение локального минимума функции.

Эти примеры показывают, что равенство нулю частных производных дей ствительно является необходимым, но не достаточным условием существования решения.

<

Символьные вычисления
Библиотека SymPy, снабжающая Python возможностью выпол нения символьных вычислений, служит ценным инструментом
|финансовых расчетов. Она особенно полезна при интерактивном
т
финансовом анализе, так как демонстрирует более высокую производительность по сравнению с численными методами.

-

Резюме
В этой главе рассматривались математические инструменты, важные с точ ки зрения финансового анализа. Например, аппроксимация функций применяется во многих областях, таких как построение факторных моделей, интерполяция кривой доходности и регрессионная оценка американских опционов
Математические инструменты

407

по методу Монте- Карло. В процессе обработки финансовых данных часто
прибегают к выпуклому программированию, когда нужно, например, настроить параметры модели оценки опционов для разных уровней котировок или
ожидаемой волатильности.
Численное интегрирование
ключевой инструмент решения многих фи нансовых задач, таких как оценка опционов и деривативов. После того как
получена риск- нейтральная мера вероятности стохастического процесса ( или
группы процессов ), задача ценообразования сводится к определению ожидаемой доходности опциона при такой мере и дисконтированию полученного
значения на текущую дату. В главе 12 мы рассмотрим методы моделирования
определенных стохастических процессов в риск - нейтральных условиях.
В главе также рассматривались методы символьных вычислений с помощью библиотеки SymPy. Символьные вычисления могут быть очень полезны ми и эффективными при решении целого ряда математических задач, таких
как интегрирование, дифференцирование и решение уравнений.



Дополнительные ресурсы
Дополнительную информацию о рассмотренных в этой главе библиотеках
Python можно найти в Интернете.



Описание функций NumPy доступно по адресу https : // docs . scipy .
org / doc / numpy / reference / .

• Информация о методах оптимизации и поиске корней уравнений с помощью модуля scipy . optimize доступна в документации к библиотеке SciPy ( https : / / docs . scipy . org / doc / scipy / reference / optimize .
html ) .

• Функции интегрирования, имеющиеся в модуле scipy . integrate,
описаны по адресу https : / / docs . scipy . org / doc / scipy / reference /
integrate . html .

• Документация к библиотеке SymPy доступна на официальном

сайте

( www . sympy . org ) .
Весь необходимый математический аппарат рассмотрен в следующей книге:

• Brandimarte, Paolo. Numerical Methods in Finance and Economics:
A MATLAB-Based Introduction, 2nd Edition ( 2006, Wiley).

408

Глава 11

ГЛАВА 12

Стохастические методы
Предсказуемость не в том , как все пойдет, а в том, как может идти.
Рахиль Фарук



В наши дни стохастический анализ
одна из основных математических
дисциплин курса экономики и финансов. На заре современной эры финан совых расчетов, преимущественно в 1970-1980-е годы, основной целью фи нансовых исследований было создание готовых аналитических решений, на пример для оценки стоимости опционов в рамках определенной финансовой
модели. За последние годы требования к вычислительным инструментам
значительно изменились. Участникам рынка важно иметь возможность оценивать не только отдельные финансовые инструменты, но и, к примеру, целые реестры деривативов. Точно так же для получения согласованной оценки
рисков по всему финансовому учреждению, будь то стоимость под риском или
поправка на кредитный риск, приходится учитывать не только финансовую
деятельность самого учреждения, но и обязательства всех его контрагентов.
Такие сложные задачи могут быть решены только с привлечением мощных
численных методов. Именно поэтому стохастический анализ в целом и метод
Монте - Карло приобрели особую важность в финансовых вычислениях.
В главе рассматриваются следующие темы.

Случайные числа
Вначале мы познакомимся с псевдослучайными числами, на которых основано большинство методов финансового моделирования.

Моделирование
В финансовых вычислениях ключевую роль играют две задачи: модели рование случайных величин и случайных процессов.
Оценка опционов

В основном мы будем оценивать два производных финансовых инструмента: европейский опцион ( на указанную дату) и американский опцион
( на указанный период времени ). Существует также бермудский опцион с
конечным набором дат исполнения.

Оценка рисков
Методы моделирования хорошо справляются с оценками рисков, такими
как стоимость под риском ( VaR) или поправка на кредитный риск ( CVA).

Случайные числа
Во всех примерах главы случайные числа1 генерируются функциями модуля numpy . random.
In [1]: import math
import numpy as np
import numpy.random as npr
from pylab import pit , mpl

In [ 2 ] : pit . style . use ( ' seaborn ' )
mpl . rcParams [ 1 font . family ' ]
%matplotlib inline

О

= ' serif '

О Импорт модуля библиотеки NumPy, отвечающего за генерирование случайных чисел.

Например, функция rand ( ) генерирует случайные числа на отрезке [ 0, 1) с
распределением, которое задается в качестве параметра. Функция возвращает
объект ndarray, что позволяет легко масштабировать полученные случайные
числа на более длинный интервал вещественных значений. В частности, ниже
показано, как получить случайные числа в диапазоне [ а , Ь ) = [5, 10) , выполняя
масштабирование результатов функции npr . randQ как в одном, так и в двух
измерениях.
In [ 3 ] : npr . seed ( 100 ) О
np . set _ printoptions ( precision = 4 )

О

In [ 4 ] : npr . rand ( lG ) ©
0ut [ 4 ] : аггау ( [ 0.5434 , 0.2784 , 0.4245 , 0.8448 , 0.0047 , 0.1216 ,
0.6707 , 0.8259 , 0.1367 , 0.5751 ] )

In [ 5] : npr . rand ( 5 , 5 )
Out [ 5] : array ( [[ 0.8913 ,
[ 0.9786 ,
[ 0.4317 ,
[ 0.3728 ,
1

©
0.2092 ,
0.8117 ,
0.94 ,
0.0057 ,

0.1853 ,
0.1719 ,
0.8176 ,
0.2524 ,

0.1084 ,
0.8162 ,
0.3361 ,
0.7957 ,

0.2197 ] ,
0.2741 ] ,
0.1754 ] ,
0.0153 ] ,

Для простоты мы будем использовать термин случайные числа , но помните о том, что все эти

числа

410

— псевдослучайные.
Глава 12

[ 0 . 5 9 8 8 , 0 . 6 0 3 8 , 0 . 1 0 5 1 , 0 . 3 8 1 9 , 0 . 0 3 6 5 ]] )
In [6]: a

=

b

=

5.
10 .

О
©

n p r . rand ( l Q ) * ( b - a ) + a ©
Out [ 6 ] : a r r a y ( [ 9 . 4 5 2 1 , 9 . 9 0 4 6 , 5 . 2 9 9 7 , 9 . 4 5 2 7 , 7 . 8 8 4 5 , 8 . 7 1 2 4 ,
8 . 1 5 0 9 , 7 . 9 0 9 2 , 5 . 1 0 2 2 , 6 . 0 5 0 1] )
I n [ 7 ] : n p r . rand ( 5 , 5 ) * ( b - a ) + a ©
Out [ 7 ] : a r r a y ( [[ 7 . 7 2 3 4 , 8 . 8 4 5 6 , 6 . 2 5 3 5 , 6 . 4 2 9 5 ,
[9.875 , 9.4243 , 6.7975 , 7.9943 ,
[6 . 7 0 1 , 5 . 8 9 0 4 , 6 . 1 8 8 5 , 5 . 2 2 4 3 ,
[6 . 8 8 1 3 , 7 . 9 6 4 , 8 . 1 4 9 7 , 5 . 7 1 3 ,
[9.7319 , 8.0115 ,6.9388 , 6.8159 ,

9.262 ],
6.774 ],
7 . 5 2 7 2] ,
9 . 6 6 9 2] ,
6 . 0 2 1 7 ]] )

О Установка затравочного значения, чтобы пример можно было легко воспроизвести, и задание количества цифр после запятой.

© Получение одномерного объекта nda г г а у с равномерным распределением.

0 Получение двухмерного объекта nda г г а у с равномерным распределением.
0 Нижний...
0 ...и верхний пределы...
© ...интервала масштабирования случайных чисел.
© Масштабирование случайных чисел в двухмерном пространстве.
Функции, применяемые для генерирования простых случайных чисел,
описаны в табл. 12.1.

Таблица 12.1. Функции генерирования простых случайных чисел
Возвращаемый результат

Функция

Параметры

rand ( )
randnQ

dO , dl ,

d 0 , dl ,

randintQ

low [

,

high , size ]

low [

,

high , size ]

_
randon_ sample ( )

random integers ( )

. . .,
...,

d /7

Случайные числа из заданного распределения

dn

Выборка ( или выборки ) из стандартного нормального распределения
Случайные целые числа из диапазона от low
( включая ) до high ( не включая )
Случайные целые числа из диапазона от low

до high включительно
[ size ]

Случайные вещественные числа из полуоткрытого интервала [0,0; 1,0)
Стохастические методы

| 411

Окончание табл. 12.1
Функция

Параметры

Возвращаемый результат

randomQ

[ size]

Случайные вещественные числа из полуоткрытого интервала [0,0; 1,0 )

ranf ( )

[ size ]

Случайные вещественные числа из полуоткрытого интервала [0,0; 1,0)

sampleQ

[ size ]

Случайные вещественные числа из полуоткрытого интервала [0,0; 1,0)

choice ( )

a[

bytes ( )

length

, size,

replace , р] Случайная выборка элементов из заданного
одномерного массива

Случайные байты данных

Визуализировать результаты, возвращаемые перечисленными функциями,
несложно. На рис. 12.1 показаны графики двух непрерывных и двух дискретных распределений, сгенерированных следующим кодом.

_

In [ 8 ] : sample size = 500
rnl = прг . rand ( sample size , 3 ) О
m 2 = прг randint ( 0 , 10 , sample size )
m 3 = npr . sample ( size = sample size ) ©
а = [ 0 , 25 , 56 , 75 , 100 ] ©
гп 4 = npr . choice ( a , size = sample size )

_

.

_

In [ 9 ] : fig , ( ( axl , ax 2 ) , ( ax 3 , ax 4 ) )

=

.
=
. _
. _
ax 2 . hist ( rn 2 , bins = 25 )
ax 2 . set _ title ( Функция

=

_

©

_

©

plt . subplots ( nrows = 2 ,
ncols = 2 , figsize = ( l 0 , 8 ) )

axl hist ( rnl , bins 25 , stacked True )
axl set title ( ' Функция rand ( ) ' )
axl set ylabel ( ' Частота ' )
1

randint ( ) 1 )

ax 3 . hist ( rn 3 , bins= 2 S )
ax 3 . set title ( ' Функция sample( ) )
ax 3 . set ylabel ( 1 Частота ' )
ax 4 . hist ( rn 4 , bins = 25 )
ax 4 . set title ( ' Функция choiceQ ' ) ;

_
_
_

1

О Случайные числа с равномерным распределением.
© Случайные числа из заданного интервала.
© Случайная выборка значений из конечного списка.

412

Глава 12

Функция rand ( )

Функция

randintO

60

80
70

50

60

40

-

50

зо



~ 40

т

30

20

Л)

10

10
О

0.2

0.4

0.6

0.8

о

0

2

Функция sampleQ

4

Функция

30

я
н
о

SП З

25

юо

20

80

15

60

10

40

5

20

6

8

choiceO

tr

О

0.2

ОА

о
0.6

0.8

О

20

40

60

80

100

Рис. 12.1 . Гистограммы распределения простых случайных чисел

В табл. 12.2 описаны функции , применяемые для генерирования случайных
чисел с другими типами распределений .
Таблица 12.2 . Функции генерирования случайных чисел, подчиняющихся
другим законам распределения
Функция

Параметры

Возвращаемый результат

beta

Выборка из бета - распределения в интервале [0, 1 ]

binomial

о, b [ , s i z e ]
n f p [ , size ]

chisquare

d f\ , s i z e ]

Выборка из распределения хи-квадрат

dirichlet

alpha[ , size ]

Выборка из распределения Дирихле

exponential

[ scale , size]

Выборка из экспоненциального распределения

f
gamma

dfnun , d f d e n [ , s i z e ]

Выборка из распределения Фишера

shape [ , scale , size ]

Выборка из гамма - распределения

geometric

p[ , s i z e ]

Выборка из геометрического распределения

gumbel

[ loc , scale , size]

Выборка из распределения Гумбеля

Выборка из биномиального распределения

Стохастические методы

413

Продолжение табл. 12.2
Функция

Параметры

Возвращаемый результат

hypergeonetric

n g o o d , nbad ,
nsanple[ , size ]

Выборка из гипергеометрического распределения

laplace

[ loc, scale, size]

Выборка из распределения Лапласа, или двойного
экспоненциального распределения

logistic

[ loc, scale, size]

Выборка из логистического распределения

lognormal

[ mean , s i g n a , s i z e ]

Выборка из логнормального распределения

logseries

P[ , size]

Выборка из логарифмического распределения

multinomial

n, pvals [

multivariate_

nean, c o v [

,

size]

Выборка из мультиномиального распределения

, size]

Выборка из многомерного нормального распределения

normal
negative_

n , p[ , s i z e ]

Выборка из отрицательного биномиального
распределения

noncentral_
chisquare

df,

Выборка из асимметричного распределения
хи-квадрат

noncentral_ f

dfnun , dfden , nonc [ , Выборка из асимметричного распределения

binomial
nonc [

, size]

size]

Фишера

normal

[ loc, scale, size]

Выборка из нормального распределения

pareto

a[

,

size]

poisson

[ l a n, s i z e ]

power

a[

, size]

Выборка из распределения Парето типа II
Выборка из распределения Пуассона
Выборка в интервале [0, 1] из степенного распределения с положительной экспонентой а - 1

[ scale, size]

Выборка из распределения Рэлея

standard cauchy

_

[ size]

Выборка из стандартного распределения Коши
с нулевой модой

standard_

[ size]

Выборка из стандартного экспоненциального
распределения

rayleigh

exponential

_

standard gamma

shape [

,

size]

Выборка из стандартного гамма-распределения

standard_normal [ s i z e ]

Выборка из стандартного нормального распределения

standard_ t

df [ , size]

Выборка из распределения Стьюдента с (// степенями свободы

l e f t , node, right [ ,
size]

Выборка из треугольного распределения в интервале [ l e f t , r i g h t ]

[ low , h i g h , s i z e ]

Выборка из равномерного распределения

triangular

uniform

414

Глава 12

Окончание табл. 12.2
Функция

Параметры

Возвращаемый результат

vonmises

пи, kappa [

Выборка из распределения Мизеса

wald

, size ]
,
nean scale [ , size ]

weibull

a[

zipf

a[

, size ]

.

Выборка из распределения Вальда, или обратного
гауссова распределения
Выборка из распределения Вейбулла
Выборка из распределения Ципфа

size ]

Несмотря на критику идеи применения нормальных распределений в фи нансовом анализе, они по -прежнему остаются самыми популярными видами
распределений в аналитических приложениях. Одна из причин заключается
в том, что многие финансовые модели непосредственно построены на базе
нормального или логнормального распределения. А те модели, в которых
( лог)нормальное распределение не используется напрямую, всегда можно
дискретизировать, а затем аппроксимировать с помощью нормального рас пределения.
В качестве иллюстрации на рис. 12.2 визуализированы случайные выборки
из следующих типов распределений.

• Стандартное нормальное распределение с математическим ожиданием
р = 0 и стандартным отклонением о - 1.

• Нормальное распределение с математическим ожиданием р = 100 и стандартным отклонением о = 20.

• Распределение хи- квадрат с 0,5 степенями свободы.
• Распределение Пуассона с математическим ожиданием А = 1.
На рис. 12.2 представлены три непрерывных и одно дискретное распреде ление (Пуассона). Последнее используется, в частности, для моделирования
таких (редких) событий, как скачки стоимости финансового инструмента или

обвал рынка.

_

In [ 18 ] : sample size = 500
rnl = npr standard_norrnal( sanple size ) О
m 2 = npr normal( 100, 20, sample size ) ©
m3 = npr chisquare ( df =0.5, size=sample_ size )
rn4 = npr poisson (lani=i 0 size=sample_ size )

.
.
.

.

_
_

.,

©
©

.

In [11] : fig, ( ( axl, ax 2 ) , ( ax 3, ax 4 ) ) = pit subplots ( nrows= 2,
ncols= 2 , figsize= ( 10, 8 ))

Стохастические методы

415

.hist ( rnl, bins=25 )
.set _ title( ' Стандартное
.set _ylabel( ' Частота ' )
.hist ( rn2, bins=2 S )

axl
axl
axl
ax 2
ax 2
ax 3
ax 3
ахЗ
ax 4
ax 4

нормальное распределение ' )

.set _title( ' Нормальное распределение ( 100, 20) ' )
.hist ( rn3, bins=25 )
. set _ title ( ' Распределение хи - квадрат ' )
. set _ylabel( ' Частота ' )
.hist ( rn4, bins=2 S)
. set _title ( ' Распределение

Пуассона ' ) ;

О Случайные числа со стандартным нормальным распределением.

© Случайные числа с нормальным распределением.
© Случайные числа с распределением хи-квадрат.
© Случайные числа с распределением Пуассона.
Нормальное распределение (100, 20)

Стандартное нормальное распределение
60
50
50
40


О

Н

о

10
30

х
т

30
20
10

10

о

о

-1

1

3

2

350

СО
Н
О

но

40

20

Распределение хи-квадрат

80

120

100

140

200

300

175

250

150
125

200

~

о

100

•Гсо 150

75

100

50

50

25

0

0
0

2

4

6

8

0

1

2

I
3

.
4

5

Рис. 12.2. Гистограммы случайных выборок из различных распределений

416

160

Распределение Пуассона

Глава 12

6

Библиотека NumPy и случайные числа
В этом разделе было показано, что библиотека NumPy играет очень
важную ( порой незаменимую ) роль в генерировании псевдослу|чайных чисел в Python. Создание массивов ndarray произвольного
т
размера, заполненных случайными числами, не только простая,
но и эффективная операция.

<

-



Моделирование



Метод Монте- Карло один из самых важных численных методов в фи нансовом анализе. В основном это обусловлено гибкостью вычисления различных математических выражений ( например, интегралов), в том числе
финансовых деривативов. Впрочем, гибкость метода достигается за счет достаточно высокой вычислительной нагрузки, поскольку для получения един ственной числовой оценки зачастую требуется выполнить сотни тысяч или
даже миллионы сложных вычислительных операций.

Случайные переменные
В качестве примера рассмотрим модель ценообразования опционов
Блэка
Шоулза Мертона. Она описывается уравнением 12.1, где ST будущий уровень фондового индекса на дату Т, a S0 текущий уровень индекса.









.

Уравнение 12.1 Моделирование будущего уровня индекса по методу Блэка
Шоулза Мертона



/V

ST = S0 exp

w

г





\

1 2\
a T +7
< л/ т Z 5
2 /
/

где

• ST — уровень индекса на будущую дату Т;
• г постоянная безрисковая краткосрочная ставка;
• о — постоянная волатильность ( равна стандартному отклонению доход-



ности) индекса;

• z—

случайная переменная со стандартным нормальным распределе нием.

Данная финансовая модель параметризуется и моделируется с помощью
следующего кода (рис. 12.3).

Стохастические методы

417

100 О
©
0.05
=
sigma = 0 . 2 5 ©
T = 2.0 ©
I = 10000 ©
ST1 = S0 * np . exp ( ( r - 0 . S * sigma ** 2 ) * T +
sigma * math . sqrt ( T ) * npr . standard normal ( I ) )

In [ 12 ] : S 0

г

=

_

©

In [ 13 ] : plt . figure ( figsize = ( 10 , 6 ) )
plt . hist ( STl , bins = S 0 )
pit . xlabel ( ' Индекс ' )
plt . ylabel ( ' Частота ' ) ;

О Начальный уровень индекса.

0 Постоянная безрисковая краткосрочная ставка.
© Постоянная волатильность.

0 Срок в долях года.
Количество траекторий.

@

© Моделирование выполняется векторизованным способом. Для дискретизации применяется функция прг . standard _ nomal ( ) .

800

500

-сЯ
и
г

Г 400

200

О

50

100

150

200

250

300

350

400

Индекс

Рис. 12.3. Результат статического моделирования геометрического
броуновского движения ( с помощью функции npr standard nornal ( ))

.

418

Глава 12

_

Из рис. 12.3 следует, что случайная величина, определяемая уравнени ем 12.1, имеет логнормальное распределение. Следовательно, можно попробовать непосредственно получить ее с помощью функции прг . lognormal ( ) ,
передав последней значения математического ожидания и стандартного отклонения.
In [ 14 ] : ST 2

S 0 * npr . lognormal ( ( r - 0 , 5 * sigma ** 2 ) * Т ,
sigma * math . sqrt ( T ) , size = I )

=

О

In [ 15 ] : plt . figure ( figsize = ( 10 , 6 ) )
plt . hist ( ST 2 , bins = 50 )
pit . xlabel ( ' Индекс ' )
pit . ylabel ( 1 Частота ' ) ;

О Моделирование выполняется векторизованным способом. Для дискретизации применяется функция npr . lognomalQ .

Результат моделирования приведен на рис. 12.4.

800

600

са
н
о
н
о

200

0

50

.

100

150

200
Индекс

250

300

350

400

Рис. 12.4 Результат статического моделирования геометрического
броуновского движения ( с помощью функции npr lognornal ( ) )

.

Рис. 12.3 и 12.4 выглядят очень похожими. Можно выполнить более строгую проверку, сравнив моменты двух распределений. Для этого мы воспользуемся модулем scipy . stats, определив вспомогательную функцию print
statistics ( ) , как показано ниже.

_

Стохастические методы

419

In [16]: import scipy.stats as scs

_

In [17]: def print statistics(al, a2):
''' Вывод статистических показателей
Параметры

al, а2: массивы пааггау
содержат результаты

моделирования

1 1 1

stal = scs.describe(al) О
sta2 = scs.describe(a2) О
print('%14s %14s %14s 1 %
(' Статистика' , 'Набор 1', ' Набор 2'))
print(45 *
print('%14s %14.3f %14.3f' % ('size ' , stal[8],
sta2[0]))
print('%14s %14.3f %14.3f ' X ('min ' , stal[l]
[Q],
sta2[l]
[0]))
prlnt('%14s %14.3f %14.3f' % ('max', stal[l]
[l],
sta2[l]
)
[1])
print('%14s %14.3f %14.3f' % (' mean ' , stal[2],
sta2[2]))
print('%14s %14.3f %14.3f' % (' std ', np.sqrt(stal[3]),
np.sqrt(sta2[3])))
print('%14s %14.3f %14.3f ' % ('skew', stal[4],
sta2[4]))
print('%14s %14.3f %14.3f' % ('kurtosis', stal[5],
sta2[5]))

_

In [18]: print statistics(STl, ST2)
Набор 1
Статистика
size
min
max
mean

std
skew
kurtosis

О

420

10000.000
32.327
414.825
110.730
40.300
1.122
2.438

Набор 2
10000.000
28.230
409.110
110.431
39.878
1.115
2.217

Функция scs . describeQ возвращает статистические характеристики
набора данных.
Глава 12

Как видим, статистические характеристики результатов моделирования
оказываются весьма схожими. Наблюдаемые различия в основном обусловлены тем, что в моделировании называется ошибкой выборки. Другой тип оши бок, возникающих при дискретном моделировании непрерывных случайных
процессов, ошибки дискретизации. В данном случае они никак не проявля ются из -за статической природы процесса моделирования.



Случайные процессы
Грубо говоря, случайный ( стохастический ) процесс представляет собой последовательность случайных величин. Соответственно, можно предположить,
что моделирование подобных процессов будет заключаться в последовательном повторном моделировании одной случайной величины. В основном так и
происходит, за тем лишь исключением, что исходы , как правило, не являются
независимыми, а зависят от результатов предыдущих исходов.
Но в целом случайные процессы, встречающиеся в финансовых вычислениях, проявляют марковское свойство, означающее, что следующее состояние
процесса зависит только от его текущего состояния, но не от каких-либо предыдущих состояний или всей цепочки состояний. Такой процесс называется

марковским.

Геометрическое броуновское движение





Мертона в динамическом виде,
Рассмотрим модель Блэка
Шоулза
который описывается стохастическим дифференциальным уравнением 12.2,
где Zf стандартное броуновское движение. Такое уравнение описывает геометрическое броуновское движение ( geometric Brownian motion GBM ). Значения St подчинены логнормальному распределению, а маржинальная доход -



ность



—dSt- — нормальному
.



распределению.

Уравнение 12.2 Стохастическое дифференциальное уравнение в модели
Блэка Шоулза Мертона

dSt = rStdt + aStdZt .
Для точной дискретизации уравнения 12.2 можно воспользоваться методом Эйлера, который представлен уравнением 12.3, где At постоянный шаг
дискретизации, a zt случайная величина со стандартным нормальным распределением.





Стохастические методы

421

.

Уравнение 12.3 Динамическое моделирование индекса в модели Блэка —
Шоулза — Мертона

s, = S, A, ехР

гг

-

1
2

\

г— а

W

2

у

At + в ) и
схождению к точке в = 0,02.
In [ 26 ] : plt . figure ( figsize = ( 10 , 6 ) )
pit . plot ( xl [ : , : 10 ] , lw=1.5 )
pit . xlabel ( 'Время' )
pit . ylabel ( 'Индекс' ) ;

0.05

0.04

-

*CD
4
к о. оз

x

0.02

0.01
0

10

40

30

20

.» ()

Время

Рис. 12.8. Динамическое моделирование траекторий диффузии
по закону квадратного корня ( метод Эйлера )

Уравнение 12.6 описывает точную схему дискретизации для диффузии
по закону квадратного корня, основанную на нецентральном распределении
хи - квадрат X ] с
f

а2

степенями свободы и параметром нецентральности

пс =

кеш

4
д
ст (1 - е-* ' )
2

^-

Стохастические методы

427

Уравнение 12.6 . Тонная схема дискретизации для диффузии по закону
квадратного корня
\
г
АД
a2 ( l - e ~ kAf )
4ке- /
- Xd
х, =
Xs
'

a \ - e^ ) У
V



\

Реализация такого уравнения на Python получится чуть более сложной, но
все равно достаточно компактной. Результат в виде гистограммы показан на
рис. 12.9.
600

500

400
Л

н
о
н
яз 300
ЕГ
200

100

0
0.01

0.02

0.03

0.04

Значение

Рис . 12.9. Динамическое моделирование диффузии по закону
квадратного корня ( точная схема )

_

In [ 27 ] : def srd exact ( ) :
х = np . zeros ( ( M + 1 , I ) )
x[ 0 ] = x 0
f o r t i n r a n g e ( l , M + 1) :
df = 4 * theta * kappa / sigma ** 2 О
c = ( sigma ** 2. * ( 1 - np . exp ( - kappa * dt ) ) ) /
( 4 * kappa ) О
nc = np . exp ( - kappa * dt ) / с * x[ t - 1 ] О
x[ t ] = c * npr . noncentral chisquare ( df , nc , size = I )
return x
x 2 = srd exact ( )

_

_

In [ 28] : plt . figure ( figsize = ( 10 , 6 ) )

428

Глава 12

О

pit . hist ( x 2 [ - 1] , bins=50 )
pit . xlabel ( ' Значение ' )
pit . ylabel ( ' Частота ' ) ;

О Точная схема дискретизации на основе функции прг . noncentral
chisquareQ.

На рис. 12.10 показаны графики первых 10 траекторий процесса, на кото рых точно так же просматривается тенденция к отрицательному дрейфу и
схождению значений к 9.
In [ 29 ] : plt . flgure ( figsize= ( 10 , 6 ) )
plt . plot ( x 2 [ : , : 10 ] , lw =1.5 )
pit . xlabel ( ' Время ' )
pit . ylabel ( ' Индекс ' ) ;

0.05

0.04

_
tr

=

0.03

1

0.02

0.01

()

10

30

20

40

50

Время

Рис. 12.10. Динамическое моделирование траекторий диффузии
по закону квадратного корня ( точная схема )

Сравнение основных статистических характеристик обоих схем позволяет
сделать вывод о том, что смещенный метод Эйлера дает достаточно хорошие
результаты с точки зрения статистики.
In [ 30 ] : print_ statistics ( xl [ - l] , х 2 [ - 1] )
Набор 1
Набор 2
Статистика

size

min

10000.000
0.003

10000.000
0.005

Стохастические методы

429

max
mean

std
skew
kurtosis

0.047
0.020
0.006
0.532
0.273

0.049
0.020
0.006
0.529
0.289

In [ 31 ] : I = 250008
% time xl = srd _euler ( )
CPU times : user 1.62 s , sys : 184 ms , total: 1.81 s
Wall time : 1.08 s
In [ 32 ] : % time x 2 = srd _ exact ( )
CPU times : user 3.29 s , sys : 39.8 ms , total: 3.33 s

Wall time : 1.98 s
In [ 33 ] : print _ statistics ( xl [ ~ l] , x 2 [ ~ l] )
xl = 0 . 0; x 2 = 0 . 0
Набор 1
Статистика

size

min
max
mean
std
skew
kurtosis

250000.000
0.002
0.071
0.020
0.006
0.563
0.492

Набор 2

250000.000
0.003
0.055
0.020
0.006
0.579
0.520

В то же время можно заметить существенную разницу с точки зрения скорости вычислений, поскольку выборка из нецентрального распределения
хи-квадрат — намного более затратный процесс, чем выборка из стандартного нормального распределения. Моделирование по точной схеме отнимает
вдвое больше времени, чем моделирование по методу Эйлера при практиче ски идентичных результатах.

Стохастическая волатильность
Главное допущение модели Блэка — Шоулза — Мертона заключается в постоянстве волатильности. В действительности волатильность не является ни
постоянной, ни детерминированной величиной — она имеет абсолютно стохастический характер. Поэтому прорыв в финансовом моделировании про изошел только в начале 1990-х годов с появлением так называемых моделей
стохастической волатильности. Одна из самых популярных моделей такого
типа — модель Хестона [ 3 ], описываемая уравнением 12.7.
430

Глава 12

Уравнение 12.7. Стохастические дифференциальные уравнения для модели
стохастической волатильности Хестона

^

dSt = rS dt + StdZ],
dv, = К ОЯ - v, ) dt +

n

S 80
60

40

\

20

о

10

20

30

40

50

Время

Puc. 12.14. Динамическое моделирование траекторий прыжковой диффузии
Стохастические методы

437

Уменьшение дисперсии
Все применявшиеся ранее функции Python позволяют генерировать только псевдослучайные числа, а поскольку размеры выборок меняются, получае мые наборы чисел далеко не всегда обладают ожидаемыми статистическими
характеристиками. Предположим, требуется получить набор случайных чи сел со стандартным нормальным распределением, в котором математическое
ожидание равно 0, а стандартное отклонение 1. Проверим, какие статисти ческие характеристики будут у различных наборов случайных чисел. Для более реалистичного сравнения зафиксируем затравочное значение генератора
случайных чисел.



In [ 52 ]: print( %15s %15s ' % (' Среднее', 'Стандартное отклонение'))
print(31 * ' - ')
for i in range ( l , 31 , 2 ) :
npr.seed ( 108 )
sn = npr.standard _ normal(i ** 2 * 10000 )
print('%15.12f %15.12f' % (sn.mean(), sn.std()))
Среднее
Стандартное отклонение
1

0.001150944833
0.002841204001
0.001998082016
0.001322322067
0.000592711311
- 0.000339730751
- 0.000228109010
0.000295768719
0.000257107789
- 0.000357870642
- 0.000528443742
- 0.000300171536
- 0.000162924037
0.000135778889
0.000182006048

1.006296354600
0.995987967146
0.997701714233
0.997771186968
0.998388962646
0.998399891450
0.998657429396
0.998877333340
0.999284894532
0.999456401088
0.999617831131
0.999445228838
0.999516059328
0.999611052522
0.999619405229

In [ 53 ] : i ** 2 * 10000
0ut[53]: 8410000

Полученные результаты свидетельствуют о том, что статистические пока затели улучшаются с увеличением количества исходов2. Но они все еще дале2

Здесь действует закон больших чисел.

438

Глава 12

ки от требуемых даже в самой большой выборке, которая насчитывает свыше
8 000 000 случайных чисел.
К счастью, существуют простые и универсальные способы уменьшения
дисперсии, позволяющие уравновесить два первых момента (стандартного)
нормального распределения. Один из них — метод симметричных выборок
( antithetic variates). В этом методе берется только половина от требуемого количества случайных значений, а затем к ним добавляется точно такой же на бор, но с обратным знаком3. Например, если генератор случайных чисел ( т.е.
соответствующая функция Python ) возвращает число 0.5, то помимо него в
конечный набор включается также значение - 0.5. В результате среднее такого
набора будет равно нулю.
В NumPy данный подход к получению случайных чисел реализует функ ция np.concatenateQ. Следующий пример повторяет предыдущий, только
теперь в нем используются симметричные выборки.

_

In [ 54 ] : sn = npr.standard nornal(int(10009 / 2 ) )
sn = np.concatenate((sn, - sn)) О
In [ 55 ] : np.shape(sn)
Out[55]: (10000,)

©

In [ 56 ] : sn . meanO ©
0ut[56]: 2.84217094304O4Ole 18

-

In [ 57 ] : print('%15s %15s' %('Среднее ' , 'Стандартное отклонение '))
print(31 * "-")
for i in range(l, 31 , 2 ) :
npr.seed(1000)
sn = npr.standard nomal(i ** 2 * int(10000 / 2 ) )
sn = np.concatenate((sn, - sn))
print(“%15.12f %15.12f” % (sn.mean(), sn.std()))
Среднее Стандартное отклонение

_

0.000000000000

- 0.000000000000

0.000000000000

- 0.000000000000
0.000000000000

- 0.000000000000
3

1.009653753942
1.000413716783
1.002925061201
1.000755212673
1.001636910076
1.000726758438

Описанный метод работает только для симметричных случайных величин с нулевой медиа ной, как в стандартном нормальном распределении.

Стохастические методы

439

- 0.000000000000

1.001621265149
1.001203722778
1.000556669784
1.000113464185
0.999435175324
0.999356961431
0.999641436845
0.999642768905
0.999638303451

0.000000000000
- 0.000000000000
- 0.000000000000
- 0.000000000000
0.000000000000
- 0.000000000000
- 0.000000000000
- 0.000000000000

-

О Конкатенация двух массивов ndarray...
© ...для получения нужного количества случайных чисел.
© Среднее результирующего набора равно нулю ( в пределах стандартной
арифметической ошибки округления чисел с плавающей точкой).

Как видите, метод симметричных выборок позволяет получить идеальный
первый момент для набора случайных чисел, что и не удивительно. Однако
он не оказывает никакого влияния на второй момент
стандартное отклонение. Существует другой способ понижения дисперсии
метод моментов
( moment matching ), с помощью которого можно за один шаг улучшить как
первый, так и второй момент.



In [ 58 ] : sn

=



_

npr . standard normal ( 10000 )

.

In [ 59 ] : sn mean ( )
0 ut [ 59 ] : - 0.001165998295162494

In [ 60 ] : sn . stdQ
Out [ 60 ] : 0.991255920204605

_

In [ 61] : sn new

= ( sn - sn . meanQ ) / sn . stdQ ©

In [ 62 ] : sn _ new . meanQ
Out [ 62 ] : - 2.3803181647963357e - 17

_

In [ 63] : sn new . std ( )
Out [ 63] : 0.9999999999999999

О Одновременная коррекция первого и второго моментов.
Если мы вычтем среднее из каждого случайного числа и разделим результат на стандартное отклонение, то добьемся почти идеального соответствия
первому и второму моментам стандартного нормального распределения.
440

Глава 12

Описанные методы понижения дисперсии реализованы в следующей
функции, которая генерирует случайные числа со стандартным нормальным
распределением.

_

_

_

In [ 6 4 ] : def gen sn ( M , I , anti paths =True , mo match = True ) :
' ' ' Функция , генерирующая случайные числа
для моделирования

.

Параметры

М : int

Количество временных интервалов дискретизации
I : int
Количество моделируемых траекторий
anti paths : булево значение
Применение симметричных выборок
mo match : булево значение
Применение метода моментов

_

_

_

1 1 1

if anti paths i s True :
sn = npr . standard nomal ( ( M + i , int ( I / 2 ) ) )
sn = np concatenate ( ( sn , - sn ) , axis = l )
else :
sn = npr standard nornal ( ( M + 1, I ) )
if mo match i s True :
sn = ( sn - sn . meanQ ) / sn . stdQ
return sn

_

.

_

s

.

_

Векторизация и моделирование
Векторизация вычислений с помощью инструментов NumPy —
удобный и эффективный способ реализации метода Монте- Карло
в Python. В то же время он оказывается очень требовательным к
ресурсам памяти. Альтернативные решения, обеспечивающие ана логичную производительность, рассматривались в главе 10.

Оценка опционов
Одно из наиболее важных применений метода Монте- Карло заключается в
оценке условных требований (опционов, фьючерсов, гибридных инструментов
и т.п.). Если объяснять простым языком, то в риск- нейтральных процессах стоимость условного требования определяется как дисконтированная ожидаемая
Стохастические методы

441

выплата в риск -нейтральной (мартингальной) мере. Это мера вероятности того,
что все факторы риска (акции, индексы и пр.) будут дрейфовать с безрисковой
краткосрочной ставкой, делая процессы дисконтирования мартингалами4 Согласно фундаментальной теореме ценообразования финансовых активов существование такой меры вероятности равнозначно отсутствию арбитража.
В финансах опцион — это право на покупку ( колл ) или продажу ( пут )

.

определенного финансового инструмента при наступлении определенной
даты ( европейский опцион ) или в течение заданного периода времени ( американский опцион ) за оговоренную цену ( цена исполнения, или страйк - цена ).
Сначала мы рассмотрим самый простой случай — ценообразование европей-

.

ского опциона

Европейские опционы
Выплаты по европейскому колл - опциону для фондового индекса на дату
исполнения рассчитываются по формуле /z ( Sr ) = шах ( 5г - К , 0), где

(
,
уровень индекса на дату исполнения Г а К — цена исполнения страйк цена).

ST

С учетом риск -нейтральной (на полных рынках) меры для соответствующего
случайного процесса (например, геометрического броуновского движения)
стоимость такого опциона определяется по формуле, описываемой уравнением 12.10.

Уравнение 12.10. Ценообразование опциона в риск - нейтральных условиях
00

С0 =

erTEi ( h ( ST ) ) = e- rT jh( s )q( s )ds.
О

Принципы численного интегрирования по методу Монте-Карло были рас смотрены в предыдущей главе. Попробуем применить этот подход к уравнению 12.10. Уравнение 12.11 позволяет получить оценку стоимости европейско го опциона по методу Монте-Карло, где ST' — Т -й смоделированный уровень
индекса на дату исполнения.
Уравнение 12.11. Риск - нейтральная оценка стоимости опциона по методу
Монте-Карло

4

.

.

О мартингалах можно прочитать в Википедии (https : / / ги Wikipedia org / wiki/Мартингал).

442

Глава 12

Теперь можно переходить к параметризации модели геометрического бро уновского движения. Функция оценки gbm_mcs_ stat ( ) имеет единственный
параметр: страйк -цена. В данном случае моделируется только уровень индекса
на дату исполнения. В качестве ориентира выберем цену исполнения К = 105.

.

In [ 65 ] : SO = 100
г = 0.05
sigma = 0 . 2 5
Т = 1.0
I = 50000

_

_

In [ 66 ] : def gbm mcs stat ( K ) :
' ' ' Оценка европейского колл - опциона в модели
Блэка - Ыоулза - Мертона по методу Монте - Карло
( уровень индекса на дату исполнения )

Параметры
К:

float
( Положительная ) страйк - цена опциона

Возвращает

СО :

float

Ожидаемая текущая стоимость европейского колл - опциона

1 1 1

_

sn = gen sn(l, I)
# Моделирование уровня индекса на дату исполнения
ST = S 0 * пр ехр( ( г - 0.5 * sigma ** 2 ) * Т +
sigma * math sqrt ( T ) * sn[1] )
# Вычисление выплаты на дату исполнения
hT = np maximum( ST - К, 0 )
# Вычисление оценки по методу Монте - Карло
С0 = math exp ( - r * Т ) * np mean( hT )

.

.

.

.

.

return СО

_

In [ 67 ] : gbm_mcs stat ( K=105 . )
0ut [ 67 ] : 10.044221852841922

О

О Оценка стоимости европейского колл- опциона по методу Монте-Карло.
Теперь можно переходить к динамическому моделированию процесса,
включив в него не только европейский колл-опцион, но и пут- опцион. Соответствующий алгоритм реализован в функции gbm_mcs _dyna ( ). В следующем
Стохастические методы

443

коде также сравниваются прогнозируемые стоимости опционов “ колл” и “пут”
для одного и того же уровня индекса.

О

In [ 68 ] : М = 50

_

In [ 69 ] : def ghm__mcs dyna ( K, option= ' call ' ) :
' ' ' Оценка европейского колл - опциона в модели
Блэка - Ыоулза - Мерпона по методу Монте - Карло
( траектории уровней индекса )
Параметры

К:

float

( Положительная ) страйк - цена опциона
option : строка
Тип оцениваемого опциона ( ' c a l l ' , ' p u t ' )
Возвращает
СО :

float

-

Оценка текущей стоимости европейского колл опциона

1 1 1

dt = Т

/

М

# Моделирование траекторий уровня индекса
S = np zeros ( ( M + 1, I) )
S [ 0 ] = SO
sn = gen sn ( M, I)
for t in range (l, M + 1) :
0.5 * sigma ** 2 )
S [ t ] = S [ t - 1] * np exp ( ( r
dt + sigma * math sqrt ( dt ) * sn[ t ] )
# Расчет выплат no каждому типу

.

_

.

.

-

if option == call ' :
hT = np .maximum( S [ -l] - K, 0 )
else:
hT = np .maximum( K - S [ ~l], 0 )
!

# Вычисление оценки no методу Монте - Карло
СО = math exp ( - r * Т ) * np mean( hT )
return СО

.

_ _

In [ 70 ] : gbm mcs dyna ( K=110
Out [ 70 ] : 7.950008525028434

444

Глава 12

.

. , option= ' call ' )

©

*

_ _

©

In [ 71 ] : gbm mcs dyna ( K = 110 . , option = ' put ' )
Out [ 71 ] : 12.629934942682004

О Количество временных интервалов дискретизации.

© Оценка стоимости европейского колл -опциона по методу Монте- Карло.
© Оценка стоимости европейского пут - опциона по методу Монте - Карло.
Вопрос в том , насколько хорошо методы моделирования оценивают стои мость опционов в сравнении с эталонным значением , которое рассчитывается
по формуле Блэка — Шоулза — Мертона? Чтобы выяснить это, в следующем
коде мы сгенерируем соответствующие значения / оценки стоимости для диа пазона страйк - цен, используя аналитическую функцию ценообразования
европейских колл - опционов , содержащуюся в модуле bsm_ functions . ру ( он
рассматривается в конце главы) .
Сначала сравним результаты статического моделирования с точными зна чениями, возвращаемыми аналитической функцией .

_

_

In [ 72 ]: from bsm _ functions import bsm call value

_
dyna _ res
anal _ res

In [ 73 ] : stat res

= [] ©
= [] ©
= [] ©

k _list = np . arange ( 88 . , 120.1 , 5 . )
np . random . seed ( 100 )

©

In [ 74 ] : for К in kjlist :
stat res . append ( gbm mcs stat ( K ) ) ©
dyna res . append ( gbm_mcs _dyna ( K ) ) ©
anal res . append ( bsm call value ( S 0 , К , T , r , sigma ) )

_
_
_

In [ 75 ] : stat _ res

_

dyna res
anal_ res

_ _
_

=
=
=

_

np . array ( stat _ res )
np . array ( dyna res )
np . array ( anal _ res )

_

©

0
О
0

© Инициализация пустых списков , предназначенных для хранения резуль татов .

© Создание массива ndarray , содержащего диапазон страйк - цен.

© Моделирование/ вычисление значений стоимости опциона для всех
страйк - цен.

0 Преобразование списков в массивы ndarray .
Стохастические методы

445

Результаты представлены на рис. 12.15. Расхождения между прогнозируемыми и точными значениями составляют менее 1% как в положительную, так
и в отрицательную сторону.

.

In [ 76 ] : plt figure( ftgsize= ( 10, 6 ) )
fig, ( axl, ax 2 ) = pit subplots ( 2 , 1, sharex=True,
figsize= ( 10, 6 ) )
axl plot ( k list, anal res, ' b 1 , label= ' Аналитические

.

_
_
.
вычисления ' )
axl . plot ( kjlist , stat _ res , ' r o ' , label= ' Статическое
моделирование ' )
axl. set _ylabel( ' Стоимость европейского колл - опциона ' )
axl.legend ( loc=G )
axl. set _ylim( bottom=0 )

wi = 1.0
ax 2. bar ( k _list - wi / 2 , ( anal res
anal_res * 100, wi)
ax 2 set _ xlabel ( ' Цена исполнения ' )
ax 2 set _ ylabel( ' Разница , % ' )
ax 2. set xlin(left= 75, right=125 ) ;
0ut [ 76 ] :

_

.
.

о

a

Ш E
CD О

S

*

/

Аналитические вычисления
Статическое моделирование

15

10

8S
2 *

5

и

0

S
О
H





л 20

a> к
c 2
®

stat _res )

_

25

о
>K

-

1.25
1.00

со 0.75

ЕГ
Е

т

0.50

а3

^

0.25

I

I

0.00
80

90

100

I
110

120

Цена исполнения

.

.

Рис 12.15 Стоимость опциона, вычисляемая аналитическим способом
и по методу Монте- Карло (статическое моделирование)

446

Глава 12

Похожая картина наблюдается и при сравнении результатов динамического моделирования с точными значениями стоимости, полученными аналити ческим способом ( рис. 12.16 ) . Опять-таки, наблюдаемая разница не превышает 1% как в положительную, так и в отрицательную сторону. В общем случае
точность оценки в методе Монте- Карло можно контролировать, меняя коли чество временных интервалов М и / или число траекторий /.
In [ 77 ] : fig , ( axl , ах 2 )

plt . subplots ( 2 , 1 , sharex = True ,
figsize = ( 10 , 6 ) )
axl . plot ( k list , anal res , ' b ' , label = ' Аналитические
вычисления ' )
axl . plot ( kjlist , dyna res , ' r o ' , label = ' Динамическое
моделирование ' )
axl . set ylabel ( ' Стоимость европейского колл - опциона ' )
axl . legend ( loc = 0 )
axl set ylim ( bottopi =8 )
wi = 1 . 0
ax 2 . bar ( k list - wi / 2 , ( anal res - dyna res ) /
anal res * 100 , wi )
ax 2 . set _xlabel ( ' Цена исполнения ' )
ax 2 . set _ ylabel ( ' Разница , % ' )
ax 2 . set xlim ( left = 75 , right = 125 ) ;

=

_

_
_

_

_

.

_

_

_

_

_



25

O

§
is



я 20

0 Я

Аналитические вычисления
Динамическое моделирование

я Я .r
О 2
яг 15
a
и с
0 О

S5
о
2S
о
S *
я
о

10

5

н

U

0

0.25
0.00

^g -0.25

1 -0.50

I

I

I

I

I

td

^

-0.75
-1.00
80

90

100
Цена исполнения

110

120

Рис . 12.16 . Стоимость опциона, вычисляемая аналитическим способом
и по методу Монте - Карло ( динамическое моделирование )

Стохастические методы

447

Американские опционы
Стоимость американского опциона рассчитывается немного сложнее по
сравнению с европейским опционом. В данном случае для получения корректного значения необходимо определить момент остановки (марковский
момент ). Математически решение такой задачи описывается уравнени ем 12.12. Формулировка задачи основана на использовании дискретной временной сетки, участвующей в дальнейшем численном моделировании. В этом
смысле корректнее говорить о бермудском опционе. Если временной интервал
стремится к нулю, то стоимость бермудского опциона сходится к стоимости
американского опциона.

Уравнение 12.12. Ценообразование американского опциона как решение
задачи оптимального момента остановки

V0 =

sup
т е { 0 , Л / , 2 Д/ ,

e
Т}

~

rTE? ( hr ( Sr ) ) .

Приведенный ниже алгоритм основан на методе наименьших квадратов
Монте - Карло ( Least -Squares Monte Carlo LSM ) и взят из статьи Лонгстаффа
и Шварца [ 4 ]. Можно показать, что стоимость американского ( бермудского)
опциона на любую заданную дату t вычисляется как Vt (s) = max [ ht { s ), Ct { s)) ,
где C, (s) = Ef ( e rA' Vl + Al ( Sf + A/ ) \ St = s }
продленная стоимость опциона при
уровне индекса St = s.
Предположим, что мы смоделировали I траекторий уровня индекса на М
. как модевременных отрезках равной длины At. Определим Yt . = e
+ /St
лируемую продленную стоимость опциона для траектории i в момент времени
t. Мы не можем непосредственно задействовать данное значение, т.к. это означало бы идеальный прогноз. Но мы можем использовать перекрестные дан ные по всем моделируемым продленным стоимостям для оценки ( ожидаемой)
продленной стоимости путем регрессии по методу LSM.
Для заданного набора базисных функций bd , d = l , ..., D продлен ная стоимость вычисляется с помощью регрессионного уравнения





~

~

. - Zvd MSt

C

'

'

D

d =1

'

'

rAtVt

где оптимальные регрессионные параметры а явля -

)*

ются решением уравнения 12.13.

Уравнение 12.13. Регрессионная оценка американского опциона по методу
наименьших квадратов
\2

m i n

а\ , п

448

Глава 12

• • •>

7

aD , t 1

/

=1 V

d= x

А (5м )

Описанный алгоритм оценки американских опционов “ колл” и “пут” мето дом наименьших квадратов (LSM) реализуется с помощью функции gbm mcs

_

_

amerQ .

_

I n [ 7 8 ] : def gbmjncs amer ( K, option= ' call ' ) :
' ' ' Оценка американского колл - опциона в модели
Блэка - Ыоулза - Мертона по методу
наименьших квадратов Монте - Карло ( LSM )
Параметры

К:

float

( Положительная ) страйк - цена опциона
option : строка
Тип оцениваемого опциона ( ' c a l l ' , ' p u t ' )
Возвращает
СО :

float

Оценка текущей стоимости американского колл - опциона

1 1 1

dt = Т / М
df = math . exp ( - r * dt )

# Моделирование уровней индекса
S = np . zeros ( ( M + 1, I) )
S [ 0 ] = S0
sn = gen_ sn ( M , I)
for t i n range (l, M + 1) :
S [ t ] = S [ t - 1] * np . exp ( ( r - 0.5
. dt + sigma * math . sqrt ( dt )
# Расчет выплат no каждому типу
i f option == ' c a l l ' :
h = np . maximum( S - K , 0 )

* sigma ** 2 ) *
* sn [ t ] )

else :
h = np . maximum ( K - S , 0 )
# Алгоритм LSM
V = np . copy ( h )
for t i n range( M - 1, 0, - 1) :
reg = np . polyfit ( S [ t ] , V [ t + 1] * df , 7 )
C = np . polyval ( reg , S [ t ] )
V [ t ] = np . where ( C > h [ t ] , V [ t + 1] * df , h [ t ] )
# Вычисление оценки no методу Монте - Карло
C0 = df * np . mean ( V [l] )
return C0

Стохастические методы

449

_

.

_

.

In [ 79 ] : gbm_pics apier ( 110 , option= ' call ' )
Out [ 79 ] : 7.721705606305352
In [ 80 ] : gbn_ncs aner ( 110 , option= ' put ' )
Out [ 80 ] : 13.609997625418051

Стоимость европейского опциона представляет нижнюю границу стоимо сти американского опциона Разница называется премией досрочного исполнения. В следующем коде сравниваются стоимости европейского и американско го пут - опционов для прежнего диапазона страйк -цен, что позволяет оценить
премию досрочного исполнения5

.

.

: ] : euro_res = [ ]
In [ 8 1

_

amer res = [ ]

_

.

In [ 82 ] : k list = np arange ( 80

. , 128.1, 5 . )

In [ 83 ] : for К in k _list :
euro_res append ( gbm mcs dyna ( K, ' put 1 ))
amer _ res append( gbm_mcs amer ( K, ' put ' ) )

_ _
_

.
.

_
_

_
.
Как показано на рис. 12.17, для рассматриваемого диапазона страйк -цен

.

In [ 84 ] : euro res = np array ( euro res )
amer res = np array ( amer _ res )

премия досрочного исполнения может достигать 10%.

.

In [ 85 ] : fig, ( axl, ах 2 ) = plt subplots ( 2, 1, sharex=True,
figsize= ( 10, 6 ) )
axl plot ( kjlist , euro_res, ' b ' ,
label= ' Европейский пут - опцион ' )
axl plot ( kjlist, amer res , ' г о ' ,
label= ' Американский пут - опцион ' )
axl set _ylabel( ' Стоимость пут - опциона ' )
axl legend ( loc =0 )

.

.
.
.
wi = 1.0
ax 2. bar ( k _list

_

_

_

- wi / 2 , ( amer res - euro res ) /
euro res * 108, wi )
ax 2 set _xlabel( ' Цена исполнения ' )
ax 2 set ylabel ( ' Премия досрочного исполнения , % ' )
ах 2 set _xlin( left =75 , right=125 );

.
.

.

5

_

_

Поскольку выплаты дивидендов не предусмотрены, для колл-опционов нет премии досрочного исполнения (нет причин досрочно исполнять опцион).

450

Глава 12

20
to

eо § 15



Европейский пут-опцион
Американский пут -опцион

Я

cr
2
2

g ocн

И)

H

>
U >
E

5

10

о

a

8

я 6
О
К
er 2
ни
^я 4

о

5
2
D

а

С

о

к

2
0

. I I I
80

90

I

100
Цена исполнения

110

120

Рис. 12.17. Сравнение американского и европейского опционов

Оценка рисков
Не менее важной областью применения стохастических методов модели рования, наряду с оценкой опционов, является управление рисками. В этом
разделе мы познакомимся со способами вычисления / оценки двух наиболее
распространенных в финансовом анализе мер риска.

Стоимость под риском





Стоимость под риском ( Value at Risk VaR ) одна из самых популярных
и одновременно противоречивых мер риска. Аналитики-практики любят ее
за интуитивную понятность, тогда как теоретики активно критикуют ее за
невозможность оценить размер убытков вне доверительного уровня ( хвост
распределения ) . Таким образом, VaR это числовое значение, выраженное в
денежных единицах ( например, USD, EUR, JPY ) и характеризующее потери
( портфеля, отдельной позиции и т.п.), которые с заданным уровнем доверия
( вероятности ) не будут превышены в течение определенного периода времени.
Рассмотрим биржевую позицию величиной 1 млн долл., для которой значение VaR составляет 50 тыс. долл, с уровнем доверия 99% на отрезке 30 дней
(один месяц ). Это означает, что в течение 30 дней ожидаемые потери не превысят 50 тыс. долларов с вероятностью 99% ( т.е. в 99 случаях из 100 ). В то же



Стохастические методы

451

время ничего не известно о том, какими будут убытки, если потери превысят
50 тыс. долларов. Например, какова вероятность того, что потери составят 100
или 500 тыс. долларов? Единственное, о чем говорит VaR, что убытки, пре вышающие 50 тыс. долларов, возникают с вероятностью 1%.
Применим модель Блэка Шоулза Мертона для оценки уровней индекса на будущий период Т = 30 / 365 ( 30 дней ). Чтобы оценить VaR, необходимо
смоделировать абсолютные значения прибылей и убытков относительно текущей позиции, представив их в отсортированном виде, т.е. от самого большого убытка до самой крупной прибыли. На рис. 12.18 показана полученная
гистограмма.







600

500

400

н0
о
(

н
со 300
У

200

100

0

-20

-10

0

10

20

30

Абсолютная доходность

.

Рис. 12.18 Смоделированные абсолютные значения прибылей и убытков
( геометрическое броуновское движение )
In [86 ] : S 0

=

100

г = 0.05
sigma = 0.25
Т = 30 / 365 .
I = 10000
In [87 ] : ST

= S 0 * пр . ехр ( ( г - 0 , 5 * sigma ** 2 ) * Т +
sigma * np . sqrt ( T ) * npr . standard _ normal ( I ) ) О

In [ 88 ] : R _gbm

452

Глава 12

=

np . sort ( ST - S0 )

©

In [ 89 ] : plt . figure ( figsize = ( 10 , 6 ) )
plt . hist ( R _ gbm , bins = 50 )
pit . xlabel ( ' Абсолютная доходность 1 )
pit . ylabel ( ' Частота ' ) ;

О Значения на конец периода, полученные для модели геометрического
броуновскогодвижения.

0 Вычисление абсолютных значений прибылей и убытков по каждой траек тории и сортировка результатов.

Располагая массивом ndarray , содержащим отсортированные результаты
моделирования, дальнейшие вычисления можно выполнить с помощью функ ции scs . scoreatpercentileQ . Нужно лишь определить соответствующие
процентили. В следующем коде процентили хранятся в списке ре res . Напри мер, значение 0.1 соответствует уровню доверия 100% - 0,1% = 99,9% В дан ном случае на 30-дневном отрезке значение VaR для уровня доверия 99,9%
равно 18.8, а для уровня 90% 8.5.



= [ О . 81, 0.1, 1. , 2.5 , 5.8 , 18.8 ]
scs . scoreatpercentile ( R __gbm , percs )
print ( ' %21s 7olls ' % ( ' Доверительный уровень ' , ' VaR ' ) )
print ( 33 * ' - ' )
for pair In zip ( percs , var ) :
print ( ' %21.2f %11.3f ' % ( 100 - palr [ 0 ] , - pair [ l ] ) )
Доверительный уровень
VaR

In [ 91] : percs

var

=

99.99
99.90
99.00
97.50
95.00
90.00

21.814
18.837
15.230
12.816
10.824
8.504

Во втором примере, в котором мы возвращаемся к прыжковой диффузии
Мертона, моделирование выполняется динамическим способом. В данном
случае, из- за того что подверженный скачкам компонент имеет отрицательное
среднее значение, моделируемые значения прибылей и убытков демонстри руют бимодальное распределение ( рис. 12.19). С точки зрения нормального
распределения здесь наблюдается толстый левый хвост.

= 30 . / 365 / М
= lamb * ( math . exp ( mu

In [ 92 ]: dt
rj
In [ 93 ] : S

=

+ 0.5 * delta ** 2 )

-

1)

np . zeros ( ( M + 1, I ) )

Стохастические методы

453

S [ 0 ] = S0
s n l = npr standard_ normal( ( M + 1, I))
sn 2 = npr standard_normal ( ( M + 1, I))
p o i = npr poisson( lamb * dt , ( M + 1, I))
for t in range (l, M + 1, 1) :
S [ t ] = S[ t - 1] * ( np exp ( ( r - r j - 0 . 5 * \
sigma ** 2) * d t + sigma * math s q r t ( d t ) * \
s n l [ t ] ) + ( np exp ( mu + delta * sn 2 [ t ] ) - 1) *
p o i[ t ] )
S [ t ] = np maximum( S [ t ] , 0 )

.

.

.

.

.

.

\

.

_

.

I n [ 94 ] : R j d = np s o r t ( S [ ~l]

-

SO )

.

I n [ 95 ] : p l t f i g u r e ( f i g s i z e= ( 16, 6 ) )
p l t h i s t ( R _ jd, bins=58 )
p i t xlabel( ' Абсолютная доходность ' )
p i t ylabel( ' Частота ' ) ;

.
.
.

1200

1000

800
я

-c
-_

я

т

600

400

200

0

-80

-60

-40

- 20

0

20

40

Абсолютная доходность

Рис. 12.19. Смоделированные абсолютные значения прибылей и убытков
( прыжковая диффузия )

При такой параметризации процесса значение VaR, определяемое на
30- дневном отрезке для уровня доверия 90%, получается почти таким же, как
и в случае геометрического броуновского движения, а вот при уровне доверия
99,9% оно оказывается более чем в три раза выше ( 70 против 18.8).

454

Глава 12

In [96]: percs = [0.01, 0.1, 1., 2.5, 5.0, 10.0]
var = scs scoreatpercentile ( R jd, percs )
print ( ' %21s %lls ' % ( ' Доверительный уровень 1 , ' VaR ' ) )
print ( 33 * ' - ' )
for pair in zip ( percs, var ) :
print ( ' %21.2 f %11.3 f ' % (10G - pair [ 0 ] , - pair [l] ) )
Доверительный уровень
VaR

.

_

99.99
99.90
99.00
97.50
95.00
90.00

76.520
69.396
55.974
46.405
24.198
8.836

Это хорошо иллюстрирует проблему оценки хвостовых рисков, часто воз никающую при работе со стандартной мерой VaR.
Дополнительной иллюстрацией служит рис. 12.20, на котором сравнивают ся две меры VaR. Легко заметить, что они ведут себя совершенно по -разному
в одном и том же диапазоне доверительных уровней.
0
1п

л

>
-50

-60

GBM

TD
0

100

-

6
4
доверительный уровень [ % ]

8

10

Рис. 12.20. Стоимость под риском, полученная для геометрического
броуновского движения ( GBM) и прыжковой диффузии ( JD)

Стохастические методы

455

.

_

In [ 97 ] : percs = list ( np arange ( 8.8 , 18 , 1, 0.1) )
gbm var = scs . scoreatpercentile ( R gbrn , percs )
jd var = scs , scoreatpercentile ( R jd , percs )

_

_

_

In [ 98 ] : plt . figure ( figsize ( 18 , 6 ) )
plt . plot ( percs , gbm var , ' b ' , lw=1.5 , label= ' GBM ' )
plt plot ( percs , jd var , ' r ' , lw = l , S , label = ' JD ' )
pit legend ( loc = 4 )
pit xlabel ( ' ( 100 - доверительный уровень ) , % ' )
plt ylabel ( ' VaR ' )
pit ylim ( ymax =8 , 8 ) ;

=_

_

.
.
.
.
.

Поправка на кредитный риск
Другими важными мерами риска служат кредитная стоимость под риском
( Credit Value -at - Risk CVaR ) и поправка на кредитный риск ( Credit Valuation
Adjustment CVA ), которая выводится из CVaR. Грубо говоря, CVaR характеризует риск невыполнения контрагентом своих обязательств, например в результате банкротства. В таком случае делаются два основных предположения:
вероятность дефолта и ( средний) уровень потерь.
Рассмотрим конкретный пример, снова обратившись к модели Блэка
Мертона. В простейшем случае принимается фиксированный
Шоулза
( средний ) уровень потерь L и фиксированная вероятность р наступления де фолта контрагента ( за год). Такой сценарий реализован в следующем коде, в
котором используется распределение Пуассона и учитывается тот факт, что
дефолт может наступить лишь раз.









In [ 99 ] : S0

=

188 .

г = 0.85

sigma = 0 . 2
Т = 1.
I = 108808

.

S0 * np exp ( ( r - 0.5 * sigma ** 2 ) * T + sigma *
np sqrt ( T ) * npr . standard normal ( I ) )

=

In [ 180 ] : ST

.

_

О

In [181] : L

=

8.5

In [102 ] : p

=

0.01

In [103 ] : D

=

npr poisson ( p * T , I )

©

In [184 ] : D

=

np . where ( D > 1 , 1, D )

©

456

Глава 12

©

.

\

О Определение уровня потерь.

© Определение вероятности дефолта.
@

Моделирование дефолтных ситуаций.

0 Ограничение дефолтных ситуаций единственным случаем.
В отсутствие дефолта риск - нейтральное значение будущего уровня индек са должно быть равно текущему значению актива ( не считая различий, свя занных с погрешностью вычислений ). Значение CVaR и текущая стоимость
актива с поправкой на кредитный риск вычисляются следующим образом.

In [105]: nath.exp( r * Т) * np.mean(ST) О
Out[105]: 99.94767178982691

-

In [ 106 ] : CVaR = math.exp(- r * T) * np.rnean(L * D * ST)
CVaR ©
Out[ 106 ]: 0.4883560258963962

_
_

-

In [ 107 ] : S0 CVA = math.exp( r * T) * np.mean((l
S0 CVA ©
0ut[107]: 99.45931576393053

©

- L * D) * ST)

©

__

In [ 108 ] : S0 adj = S0 - CVaR ©
S0 adj ©
Out [ 108 ]: 99.5116439741036

О Дисконтированное среднее смоделированное значение актива в момент
времени Т.

0 Значение CVaR как дисконтированное среднее будущих потерь в случае
дефолта.

0 Дисконтированное среднее смоделированное значение актива в момент
времени Т, скорректированное на смоделированные потери от дефолта.

© Текущая цена актива, скорректированная смоделированным значением
CVaR.
В данном примере потери, связанные с кредитными рисками, наблюдаются приблизительно в 1000 случаях, что и следовало ожидать, учитывая пред полагаемую вероятность дефолта 1% и 100 000 моделируемых траекторий.
Полное частотное распределение потерь, вызванных дефолтом, приведено на
рис. 12.21. Конечно, в подавляющем большинстве случаев ( примерно 99 000 из
100 000 ) никаких потерь не происходит.
Стохастические методы

457

_

.

In [ 109 ] : np count nonzero ( L * D * ST )
Out [109 ] : 978
In [ 110 ] : plt
plt
pit
pit
pit

О

. figure ( figsize= ( 10 , 6 ) )
. hist ( L * D * ST , bins= 50 )
. xlabel ( ' Убытки ' )
. ylabel ( ' Частота ' )
. ylim ( ymax =175 ) ;

О Количество дефолтных событий и связанных с ними потерь.
160
140
120

«
н
о
н
о

$

80

60

40

20
0
0

20

40

80

Убытки

.

.

Рис 12.21 Убытки, вызванные риск- нейтральным ожидаемым
дефолтом ( индекса )

Теперь рассмотрим случай европейского колл -опциона. Его стоимость равна 10.4 при страйк - цене 100. Тогда показатель CVaR будет составлять 0.05
при тех же самых предположениях, касающихся вероятности наступления дефолта и уровне потерь.
In [ 111] : К

hT

=

=

180 ,
np . maximum ( ST

- К,

8)

In [ 112 ] : С0 = math . exp ( - r * Т ) * np . mean ( hT )
С0 О
0ut [112 ] : 10.396916492839354

458

Глава 12

О

.

.

In [ 113 ] : CVaR = math exp ( - r * T ) * np mean ( L * D * hT )
CVaR ©
Out [113 ] : 0.05159099858923533

_

.

-

In [ 114 ] : C0 CVA = math exp ( r * T ) * np . mean ( ( l
C0 CVA ©
0ut [ 114 ] : 10.34532549425012

_

-

©

L * D ) * hT )

©

О Оценка стоимости европейского колл-опциона, полученная методом
Монте- Карло.

Q

Значение CVaR как дисконтированное среднее будущих потерь в случае
дефолта.

© Оценка стоимости европейского колл-опциона, полученная методом
Монте- Карло и скорректированная с учетом возможных потерь от де-

фолта.

По сравнению с обычным активом опцион проявляет несколько иные ха рактеристики. Потери, вызванные дефолтом, наблюдаются примерно в 500
случаях, несмотря на то что всего насчитывается 1000 дефолтных событий.
Это обусловлено высокой вероятностью нулевых выплат по опциону при его
экспирации. Как показано на рис. 12.22, показатель CVaR для опциона имеет
совершенно иное частотное распределение, чем в случае обычного актива.

.

_

.

_

In [ 115 ] : np count nonzero ( L * D * hT )
Out [ 115 ] : 538
In [ 116 ] : np count nonzero ( D )
0 ut [116 ] : 978

_

©

©

In [ 117 ] : I - np . count nonzero ( hT )
0ut [ H 7 ] : 44123

©

.
.
.

In [ 118 ] : plt figure ( figsize = ( l 0 , 6 ) )
plt hist ( L * D * hT , bins = 50 )
pit xlabel ( ' Убытки ' )
pit . ylabel ( ' Частота ' )
pit . ylim ( ynax = 350 ) ;

О Количество потерь, вызванных дефолтом.

© Количество дефолтных событий.
© Количество случаев безрезультатой экспирации опциона (с нулевыми
выплатами) .

Стохастические методы

459

350

300

250

со 200
н

о
н
о

со

^ 150
100

50

0

О

10

20
Убытки

30

40

Рис. 12.22. Убытки, вызванные риск- нейтральным ожидаемым
дефолтом ( колл-опциона )

Общий сценарий Python
Ниже показана реализация основных аналитических функций, связанных
с ценообразованием европейских колл -опционов в модели Блэка Шоулза
Мертона. Подробное описание модели приведено в работах Блэка и Шоулза
[1], а также Мертона [ 5]. Альтернативный способ реализации на основе класса
Python приведен в приложении Б.



#
# Оценка европейского колл - опциона в модели
# Блэка - Ыоулза - Мертона ( BSM ) , включая функцию
# вычисления веги и функцию прогнозирования
# ожидаемой волатильности
#
# bsm_functions ру
#
# ( с ) Dr Yves 3 H i l p i s c h
# Python for Finance , 2nd ed .
#

.

.

460

Глава 12

.



_

_

def bsro call vaiue(S0, К, T, г, sigma):
''' Оценка европейского колл - опциона в модели
Блэка - Шоулза - Мертона, аналитическая формула.
Параметры

SO:
К:

float

Начальный уровень акции/ индекса

float

Страйк цена
Т: float
Срок исполнения (в долях года)
3: float
Постоянная безрисковая краткосрочная ставка
sigma: float

-

Коэффициент волатильности компонента диффузии

Возвращает

value: float
Текущая стоимость европейского колл - опциона
1 1 1

from math import log, sqrt, exp
from scipy import stats
SO = float(SO)
dl = (log(SO / K) + (r + 0.5 * sigma ** 2) * T) /
(sigma * sqrt(T))
d2 = (log(SO / K) + (r - 0.5 * sigma ** 2) * T) /
(sigma * sqrt(T))
#

stats.norm.cdf - > функция распределения для
нормального распределения

value = (SO * stats.norm.cdf(dl, 0.0, 1.0) К * exp(- r * T) * stats.norm.cdf(d2, 0.0, 1.0))
return value

_

def bsm vega(SO, К, T, r , sigma):

''' Вега европейского колл опциона в

Блэка

-

Шоулза

-

-

модели

Мертона.

Стохастические методы

461

Параметры
50:

float

Начальный уровень акции/индекса

К:

float

Т:

float

3:

float

Страйк цена

-

Срок исполнения (в

долях года)

Постоянная безрисковая краткосрочная ставка
sigma: float

Коэффициент волатильности компонента диффузии

Возвращает
vega:

float

Частная производная формулы BSM по коэффициенту сигма
(т.е. вега)

1 1 1

from math import log, sqrt
from scipy import stats
SO = float(SO)
dl = (log(S0 / К) + (г + 0 . 5 * sigma ** 2) * T) /
(sigma * sqrt(T))
vega = SO * stats.norm.pdf(dl, 0.0, 1.0)* sqrt(T)
return vega
#

Функция вычисления ожидаемой волатильности

_ __ _

_

def hsm call imp vol(SO, К, T, г, СО, sigma est, it=100):
''' Ожидаемая волатильность европейского колл - опциона
в модели Блэка - Ноулза - Мертона.
Параметры

SO: float

Начальный уровень акции/индекса

К:

float

Т:

float

3:

float

Страйк цена

-

Срок исполнения (в

долях года)

Постоянная безрисковая краткосрочная ставка

462

Глава 12

_

sigma est : float
Прогнозируемая ожидаемая волатильность

it: int
Число итераций
Возвращает

_

sigma est : float
Численный прогноз ожидаемой волатильности
1 1 1

for i in range (it ) :
sigma est - = ( ( bsm_call value ( S 0, К, T, r , sigma_est ) - C0 )
bsm vega ( S 0, К , T, r , sigma est ) )

_

_

_

_

/

return sigma_est

Резюме
В этой главе рассказывалось о применении метода Монте - Карло в финан совом анализе. Сначала было показано, как генерировать псевдослучайные
числа на основе различных законов распределения. Далее рассматривались
способы моделирования случайных величин и стохастических процессов, что
находит широкое применение во многих финансовых задачах. Мы углубленно
изучили две прикладные области: оценка европейских и американских опци онов и оценка мер риска, таких как стоимость под риском ( VaR) и поправка на
кредитный риск ( CVA).
Мы убедились в том, что Python в сочетании с библиотекой NumPy прекрасно подходит для реализации даже таких вычислительно сложных задач,
как оценка американских опционов по методу Монте - Карло. Это в основном
связано с тем, что большинство функций и классов NumPy реализовано на
С, за счет чего можно добиться намного более высокой производительности по сравнению с чистым кодом Python. Дополнительным преимуществом
становится компактность и понятность кода благодаря векторизованным

операциям.

Стохастические методы

463

Дополнительные ресурсы
Исходная статья, в которой рассматривается применение метода Мон те- Карло в финансовом моделировании:

• Boyle, Phelim. “ Options: A Monte Carlo Approach”
Economics, Vol. 4, No. 3, pp. 323-338 ).
В главе упоминались следующие источники.

( 1977, Journal of Financial

1. Black, Fischer, and Myron Scholes. “ The Pricing of Options and Corporate
Liabilities” ( 1973, The Journal of Political Economy, Vol. 81, No. 3, pp. 637-654).

.

John, Jonathan Ingersoll, and Stephen Ross.

“A Theory of the Term

,
,
(
Structure of Interest Rates 1985 Econometrica Vol. 53, No. 2, pp. 385-407).

2 Cox,

.

3 Heston, Steven. “ A Closed - Form Solution for Options with Stochastic Volatility
with Applications to Bond and Currency Options” ( 1993, The Review of
Financial Studies, Vol. 6, No. 2, pp. 327-343).

.

4 Longstaff, Francis, and Eduardo Schwartz. “ Valuing American Options by
Simulation: A Simple Least Squares Approach” ( 2001, Review of Financial

Studies, Vol. 14, No. 1, pp. 113-147 ).
5. Merton , Robert. “ Theory of Rational Option Pricing” ( 1973, Bell
Economics and Management Science, Vol. 4, pp. 141-183).

Journal of

6. Merton, Robert. “ Option Pricing When the Underlying Stock Returns Are
Discontinuous” ( 1976, Journal of Financial Economics, Vol. 3, No. 3, pp. 125144).

В следующих книгах более подробно раскрыты темы, рассмотренные в этой
главе (в первой книге не рассматриваются технические детали реализации).

• Glasserman , Paul. Monte Carlo Methods in Financial Engineering
Springer ).
• Hilpisch, Yves. Derivatives Analytics with Python ( 2015, Wiley).

( 2004,

Исчерпывающее описание кредитных рисков приведено в следующей книге:

• Duffie, Darrell, and Kenneth Singleton. Credit Risk : Pricing, Measurement, and
Management ( 2003, Princeton University Press).

464

Глава 12

ГЛАВА 13

Статистический анализ
С помощью статистики я могу доказать что угодно, кроме истины.

Джордж Каннинг



Статистический анализ
это обширная область знаний. Соответствующие инструменты и получаемые с их помощью результаты оказываются неза менимыми при работе с финансовыми данными. Это объясняет популярность
таких предметно-ориентированных языков программирования, как R. Чем
сложнее становятся применяемые статистические модели, тем выше потребность в простых и производительных решениях.
Даже целая глава не позволит в полной мере раскрыть все многообразие
данной области математики. Поэтому, как и во многих других главах, мы
сконцентрируемся только на самых важных темах и рассмотрим применение
Python для решения конкретных финансовых задач. Ключевых тем будет четыре.

Нормальное распределение
Большинство популярных финансовых моделей, таких как портфельная
теория Марковица и модель ценообразования капитальных активов, основывается на предположении о нормальном распределении значений
доходности ценных бумаг. Следовательно, необходимо иметь возмож ность проверить, соответствует ли временной ряд нормальному распределению.
Оптимизация портфеля

Портфельная теория Марковица считается одним из самых больших
достижений в финансовой статистике. Разработанная в начале 1950-х
годов пионером отрасли Гарри Марковицем, эта теория была призвана
противопоставить точные математические расчеты и статистические
методы анализа распространенной на тот момент практике принятия
самостоятельных инвестиционных решений. В определенном смысле
она стала первой настоящей количественной моделью, получившей ши рокое распространение в финансовой отрасли.

Байесовская статистика
На концептуальном уровне байесовская теория вводит понятие степени
доверия к случайному событию, которая может меняться при получении
новых статистических данных. Применительно к линейной регрессии это
может означать наличие статистического распределения для регрессионных параметров (таких, как угловой коэффициент и коэффициент сдвига
регрессионной прямой) вместо оценочных значений отдельных точек. На
сегодняшний день байесовские методы находят широкое применение в
финансах, и в этом разделе мы рассмотрим несколько примеров.

Машинное обучение
Машинное обучение базируется на передовых статистических методах и
относится к классу методов искусственного интеллекта ( ИИ ). Подобно
математической статистике машинное обучение предлагает обширный
набор методик исследования наборов данных и построения прогнозов
на основе полученных результатов. Алгоритмы машинного обучения
могут относиться к разным направлениям, таким как обучение с учителем или обучение без учителя. Типы задач, решаемых с помощью таких
алгоритмов, тоже различаются. Самые популярные задачи кластеризация и классификация. Мы будем рассматривать алгоритмы классификации на основе обучения с учителем.



В этой главе мы будем обрабатывать значения даты / времени. Соответствующие инструменты Python , включая библиотеки NumPy и pandas, описаны в
приложении А.

Нормальное распределение
Нормальное распределение играет важнейшую роль в финансовых вычислениях, это одна из основ финансовой теории. На предположении о том, что
доходность финансового инструмента подчиняется нормальному распределению1, основаны следующие финансовые модели.

Портфельная теория Марковица
В случае нормального распределения доходностей акций для принятия
инвестиционных решений ( касательно оптимального состава портфеля )
1



Другая ключевая гипотеза линейность. Например, предполагается, что финансовые рынки,
как правило, демонстрируют линейную зависимость между спросом на акции и уплачиваемой за них ценой. Другими словами, в большинстве случаев рынки считаются полностью
ликвидными в том смысле, что меняющийся спрос не оказывает никакого влияния на удельную стоимость финансового инструмента.

466

Глава 13

достаточно ориентироваться только на ( ожидаемую ) среднюю доходность, дисперсию ( волатильность ) доходностей и ковариацию между доходностями различных акций.

Модель ценообразования капитальных активов
Опять-таки, при нормальном распределении доходностей акций цены
отдельных позиций можно поставить в линейную зависимость от фон дового индекса. Такая зависимость обычно выражается с помощью бета -коэффициента (характеризует зависимость между доходностью отдельной ценной бумаги и доходностью всего рыночного портфеля ).
Гипотеза эффективного рынка
Эффективным считается рынок, на котором курсовые стоимости ценных бумаг отражают всю доступную информацию, при этом понятие
“ вся доступная информация” можно трактоваться более узко или более
широко ( например, “ вся общедоступная информация ” или “ вся инсай дерская информация ” ). Если гипотеза справедлива, то цены на акции
колеблются случайным образом, а распределение доходностей оказыва ется нормальным.



Модель ценообразования опционов Блэка Шоулза
Броуновское движение эталонная модель, применяемая для изучения
флуктуаций цен финансовых инструментов. В общеизвестной формуле
оценки опционов Блэка Шоулза Мертона модель геометрического
броуновского движения используется для описания флуктуаций цен во
времени, что ведет к логнормальному распределению цен и нормальному распределению доходностей.







Даже этот далеко не полный список позволяет оценить важность предположения о нормальном распределении данных.

Эталонный портфель
Чтобы подготовить почву для дальнейшего анализа, начнем с исследова ния геометрического броуновского движения как одного из хрестоматийных
стохастических процессов, применяемых в финансовом моделировании. Для
траекторий процессов, моделируемых геометрическим броуновским движением S, справедливы следующие утверждения.

Нормальное распределение логарифмических доходностей
$

Логарифмические доходности log -

- = log St - log Ss для периода време-

^

ни 0 < s < t подчинены нормальному распределению.
Статистический анализ

467

Логнормальное распределение стоимостей
Для любого значения t > 0 стоимости St подчиняются логнормальному
распределению.

Прежде чем выполнять примеры, необходимо задать настройки вывода и
импортировать ряд пакетов, включая scipy.stats(http://docs.scipy.org/
doc/scipy/reference/stats.html)и statsmodels.api(http://statsmodels.
sourceforge.net/stable/).
In [1]: import math
import numpy as np
import scipy.stats as scs
import statsmodels.api as sm
from pylab import mpl, pit

In [2]: pit.style.use('seaborn')
mpl.rcParams['font.family '] = ' serif '
%matplotlib inline

В следующем коде моделируемые траектории геометрического броунов-

ского движения(см. главу 12), рассчитываемые по методу Монте-Карло, генерируются с помощью функции gen paths().

_

In [3]: def gen paths(SO, r, sigma, T, M, I):
''' Генерирование траекторий геометрического

броуновского

движения по методу Монте - Карло

Параметры

SO:
3:

float

Начальный уровень акции/ индекса

float

Постоянная краткосрочная ставка
sigma: float
Постоянная волатильность
Т: float
Конечный временной горизонт

М: int
Количество временных шагов/ интервалов
I: int
Количество моделируемых траекторий

468

Глава 13

Возвращает

paths : ndarray , shape ( M + 1, I )
Траектории , смоделированные согласно заданным параметрам
1 1 1

dt = Т / М
paths = np zeros ( ( M + 1, I) )
paths [ 0 ] = SO
for t in range (l, M + 1) :
rand = np random standard normal (I)
rand = ( rand - rand meanQ ) / rand stdQ О
paths [ t ] = paths [ t - 1] * np . exp ( ( r - 0 , 5 * sigma **
2 ) * dt + sigma * math sqrt ( dt ) * rand ) ©
return paths

.

.

.

.

_

.

.

О Сопоставление первого и второго моментов.
Q

Векторизованная дискретизация геометрического броуновского движе ния по методу Эйлера.

Процесс моделирования основан на параметризации модели по методу
Монте-Карло, как показано ниже. Функция gen_paths ( ) генерирует 250 000
траекторий, каждая из которых состоит из 50 временных шагов. Графики первых десяти траекторий приведены на рис. 13.1.

120

110

о 100

*
X

S
90

ВО

70

О

10

30

20

40

г> о

Время

.

.

Рис 13.1 Десять первых траекторий геометрического

броуновского движения
Статистический анализ

469

In [4]: SO = 1 0 0.
г = 0.0S

О
©

sigma = 0 . 2 ©
T = 1.0 О
М = SO ©
I = 250000 ©
np.random.seed(1000)

In [5]: paths = gen _ paths(S0, r , sigma, T, M, I)
In [6]: S0 * math.exp( r * T)
Out [ 6 ] : 105.12710963760242

©

In [ 7 ] : paths[ - l ].mean() ©
Out[?]: 105.12645392478755
In [ 8 ] : plt.figure(figsize=(10, 6 ) )
pit.plot( paths[:, :10])
plt.xlabel( ' Время ')
plt.ylabel(' Индекс ' );

О Начальное значение для моделируемых процессов.
0 Постоянная краткосрочная ставка.

© Постоянный коэффициент волатильности.
О Временной горизонт ( интервал) в долях года.
© Количество временных шагов.

© Количество моделируемых процессов.
© Ожидаемое значение и среднее смоделированное значение.
Главный интерес для нас будет представлять распределение логарифмических доходностей. На данный момент данные о логарифмической доходности,
полученные при моделировании траекторий, хранятся в объекте ndarray.
Ниже приведены значения логарифмической доходности для отдельной тра -

ектории.
In [9]: paths[:, 0].round(4)
, 97.821 , 98.5573, 106.1546, 105.899 ,
0ut[9]: аггау([100.
99.8363,
100.0145, 102.6589, 105.6643, 107.1107, 108.7943,

470

Глава 13

108.2449 ,
106.4105 ,
109.5725 ,
112.9766 ,
112.6271,
112.7502 ,
117.3705 ,
117.9185 ,
105.7822 ,
105.1585 ,
108.3284 ,
107.0077 ,
97.135 ,
95.4254 ,

_

In [ 10 ] : log returns

=

101.0575 ,

102.0197 ,

102.6052 ,

109.6419

113.0225 ,

112.5476 ,

114.5585 ,

109.942

116.3453 ,

115.0443 ,

113.9586 ,

115.8831

110.5539 ,

109.9687 ,

104.9957 ,

108.0679

104.3304 ,

108.4387 ,

105.5963 ,

108.866

106.0034 ,

104.3964 ,

101.0637 ,

96.4271 ,

.

98.3776

96.3386 ] )

np log ( paths [ l : ]

/

paths [ : - l ] )

_

In [ 11] : log returns [ : , 0 ] . round ( 4 )
Out [ 11] : аггау ( [ - 0.022 , 0.0075 , 0.0743 , - 0.0024 ,
0.0261,
0.0289 , 0.0136 , 0.0156 , - 0.0051,
0.0095 ,
0.0057 , 0.0663 , - 0.0006 , 0.0306 ,
0.0177 ,
- 0.0411, 0.0241, 0.0011, 0.0314 ,
0.0167 ,
0.0128 , 0.0047 , 0.0645 , 0.0053,
- 0.0214 ,
- 0.0059 , - 0.0079 , 0.0386 , - 0.0266 ,
- 0.0123 ,
- 0.0094 , - 0.0153 , - 0.0324 , - 0.0269 ,
0.0104 ,
- 0.0009 ] )

-

-

- 0.059

, 0.0018 ,

- 0.0171, - 0.0516 ,
0.0004 ,

- 0.0042 ,

- 0.0112 , - 0.0095,
- 0.0463 ,

0.0288 ,

0.0305 ,

- 0.0049 ,

- 0.0127 , - 0.0178 ,

Такой результат вполне можно получить на реальных финансовых рын ках: в одни дни инвестиции в акции приносят прибыль, а в другие вы терпите
убытки от предыдущих вложений.
Функция prlnt _statistics ( ) — это оболочка для функции scs .
describe ( ) из пакета scipy . stats . Она генерирует наглядную статистическую сводку для заданного набора данных ( исторических или смоделирован ных) , включающую такие показатели , как среднее, коэффициент асимметрии,
коэффициент эксцесса и др.

Статистический анализ

471

I n [ 13 ] : def print _ statistics ( array ) :
' ' ' Вывод основных статистических показателей

.

Параметры

array : ndarray

Объект , для которого генерируются
статистические показатели
1 1 1

.

sta = scs describe ( array )
print ( ’ %14 s 9615 s ' % ( ' Статистика ' , ' Значение ' ) )
print ( 30 * ' - ' )
print ( ' %14 s %15.5 f ' % ( ' size ' , sta [ 8 ] ) )
print ( ' %14 s %15.5 f ' % ( ' min ' , sta [l][ 0 ] ) )
print ( ' %14 s %15.5 f ' % ( ' max ' , sta [l][l] ) )
print ( ' %14 s %15.5 f ' % ( ' mean ' , sta [ 2 ] ) )
print ( ' %14 s %15.5 f ' % ( ' std ' , np sqrt ( sta [ 3 ] )) )
print ( ' %14 s %15.5 f ' % ( ' skew ' , sta [ 4 ] ) )
print ( ' %14 s %15.5 f ' % ( ' kurtosis ' , sta [ 5 ] ) )

.

.

I n [ 14 ] : print _ statistics ( log_ returns flatten( ) )
Статистика
Значение

size
min
max

mean
std
skew
kurtosis

12500000.00000
- 0.15664
0.15371
0.00060
0.02828
0.00055
0.00085

.

I n [ 15 ] : log_ returns mean ( ) * M + 8.5
Out [ 15 ] : 0.05000000000000005

.

.

*

sigma ** 2

I n [ 16 ] : log_ returns std( ) * math sqrt ( M )
Out [ 16 ] : 0.20000000000000015

О

©

О Среднегодовая логарифмическая доходность после коррекции по форму-

.

ле Ито2

© Годовая волатильность, т.е. годовое стандартное отклонение логарифмической доходности.

2

Основы стохастического исчисления Ито описаны у Глассермана [ 6].

472

Глава 13

В рассмотренном выше случае набор данных состоит из 12,5 млн точек, представленных преимущественно значениями из диапазона от -0,15 до +0,15. Для
него можно ожидать среднегодовые значения средней доходности на уровне
0,05 ( после коррекции по формуле Ито) и стандартного отклонения ( волатильности ) на уровне 0,2. В итоге годовые значения почти полностью соответствуют этому ( умножьте среднее значение на 50 и скорректируйте его по формуле
Ито, а стандартное отклонение умножьте на 50 ). Одна из причин настолько
хорошего соответствия заключается в согласовании моментов при понижении
дисперсии в процессе генерирования случайных чисел ( см. главу 12 ).
На рис. 13.2 приведено сравнение частотного распределения смоделированных значений логарифмической доходности с плотностью нормального
распределения вероятностей ( НРВ) для заданных значений параметров г и
sigma . Все необходимые вычисления выполняются функцией norm . pdf ( ) из
пакета scipy stats. Легко заметить, что совпадение распределений достаточ но точное.

V

.

НРВ
Частота

14

12

10

-сТ

8

w

I)

2

0

0.15

0.10

-0.05
0.00
0.05
Логарифмическая доходность

0.10

0.15

Рис. 13.2. Гистограмма логарифмической доходности в модели

геометрического броуновского движения и график плотности нормального
распределения вероятностей

.

_

In [ 17 ] : plt figure ( figsize = ( l 0 , б ) )
plt . hist ( log returns . flatten ( ) , bins = 70 , normed = True ,
label = ' Частота ' , color = ' b ' )
pit xlabel ( ' Логарифмическая доходность 1 )

.

Статистический анализ

473

pit . ylabel ( ' Частота ' )
= np linspace( plt . axis ( ) [ 0 ] , plt axis ( ) [ l ] )
plt . plot ( x , scs norm pdf ( x , loc= r / M , scale = sigrna /
np sqrt ( M ) ) , ' г ' , lw= 2.0 , label = ' HPB ' ) О
plt . legend ( ) ;

.

x

.

.

.

.

О Построение графика плотности нормального распределения вероятностей для предполагаемых параметров, масштабированных на длину ин тервала.

Сравнение частотного распределения ( гистограммы ) с теоретической плотностью вероятности это не единственный графический способ выявления
нормального распределения. Другое полезное средство
так называемые
графики “ квантиль квантиль” ( Q-Q ). В следующем примере смоделированные квантили сравниваются с теоретически рассчитанными. Для набора зна чений с нормальным распределением подавляющее большинство точек будет
лежать в непосредственной близости к теоретическому графику, представлен ному прямой линией ( рис. 13.3).







_

.

In [ 18 ] : sm . qqplot ( log returns flatten ( ) [ :: 500 ] , line ' s ' )
pit xlabel ( ' Теоретически рассчитанные квантили ' )
pit . ylabel ( ' Выборочные квантили ' ) ;

.

=

0.10

к

с
К

н

Рн

0.05

CU
PQ

*0

3

к

0.00

о
а
о
vo -0.05
0
32

-0.10

-4

-3

-2
-1
0
3
1
2
Теоретически рассчитанные квантили



Рис. 13.3. График “ квантиль квантиль” для логарифмической
доходности в модели геометрического броуновского движения
474

Глава 13

4

Как бы ни были привлекательны графические способы анализа, они, как
правило, не могут стать заменой более строгим процедурам тестирования.
Применяемая в следующем примере функция normality _ tests ( ) выполняет
сразу три статистических теста.

Проверка коэффициента асимметрии ( skewtest ( ))
В этом тесте проверяется “ нормальность” коэффициента асимметрии
для выборочных данных (т.е. близок ли он к нулю).
Проверка коэффициента эксцесса (kurtosistestQ )
В этом тесте проверяется коэффициент эксцесса для выборочных данных (опять-таки, близок ли он к нулю).
Проверка нормального распределения (normaltestQ )
Объединяет две предыдущие проверки.

Полученные в результате проверки P- значения свидетельствуют о том, что
логарифмическая доходность, описываемая геометрическим броуновским
движением, действительно подчиняется нормальному распределению (все
критерии больше 0,05).
In [ 19 ] : def norroality _ tests ( arr ) :
' ' ' Проверка нормального распределения для набора данных

.

Параметры

array : ndarray
Объект , для которого генерируются
статистические показатели
1 1 1

print ( ' Коэффициент асимметрии набора данных %14.3 f '
% scs . skew ( arr ) )
print ( ' P - значение коэффициента асимметрии %14.3 f ' %
scs . skewtest ( arr ) [ 1] )
print ( 1 Коэффициент эксцесса набора данных %14.3 f '
% scs . kurtosis ( arr ) )
print ( 1 Р - значение коэффициента эксцесса %14.3 f ' %
scs . kurtosistest ( arr ) [l] )
print ( ' P - значение критерия нормальности %14.3 f ' %
scs . normaltest ( arr ) [ l ] )
In [ 20 ] : normality_ tests ( log_ returns . flatten ( ) )
Коэффициент асимметрии набора данных
Р - значение коэффициента асимметрии

О
0.001
0.430

Статистический анализ

475

Коэффициент эксцесса набора данных
Р - значение коэффициента эксцесса
Р - значение критерия нормальности

0.001
0.541
0.607

О Все P-значения намного больше 0,05.
Наконец, необходимо проверить, подчиняются ли значения на конец периода логнормальному распределению. Такая проверка сводится к определению критерия нормальности: достаточно прологарифмировать данные и
убедиться в том, что полученные значения имеют ( или не имеют ) нормальное
распределение. На рис. 13.4 показаны гистограммы уровней индекса на конец
периода, как подчиняющиеся логнормальному распределению, так и пролога-

рифмированные напрямую.
In [ 21] : f , ( axl , ах 2 ) = pit . subplots(1 , 2 , figsize = ( 10 , 6 ) )
axl . hist ( paths [ - l ] , bins = 30 )
axl . set xlabel ( ' Индекс 1 )
axl . set ylabel ( ' Частота ' )
axl . set_ title ( ' Обычная шкала ' )
ax 2 . hist ( np . log ( paths[ - 1 ] ) , bins = 30 )
ax 2 . set _xlabel ( ' Логарифмический индекс ' )
ax 2 . set title ( ' Логарифмическая шкала ' )

_
_

_

Обычная шкала

30000

Логарифмическая шкала

30000
25000

25000
20000
20000
ей
Н
О

н

jo

15000
15000

10000

10000

5000

5000

0

100

150
Индекс

200

3.75 4.00 4.25 4.50 4.75 5.00 5.25 5.50
Логарифмический индекс

Рис. 13.4. Гистограммы уровней индекса на конец периода, полученные
путем моделирования геометрического броуновского движения

476

Глава 13

Статистические показатели исходного набора данных демонстрируют
вполне ожидаемое поведение, например среднее значение близко к 105. Значе ния логарифмического индекса имеют коэффициенты асимметрии и эксцесса,
близкие к нулю, и демонстрируют высокие P- значения, что подтверждает ги -

потезу о нормальном распределении .
In [ 22 ] : print _ statistics ( paths [ - 1] )
Статистика

Значение

size

250000.00000
42.74870
233.58435
105.12645
21.23174
0.61116
0.65182

nin
пах
mean

std
skew
kurtosis

_

.

In [ 23 ] : print statistics ( np log ( paths[ -l] ) )
Статистика
Значение
size

nin
nax
mean

std
skew
kurtosis

_

250000.00000
3.75534
5.45354
4.63517
0.19998
- 0.00092
- 0.00327

.

In [ 24 ] : normality tests ( np log( paths [ -l] ) )
Коэффициент асимметрии набора данных
Р - значение коэффициента асимметрии
Коэффициент эксцесса набора данных
Р - значение коэффициента эксцесса
Р значение критерия нормальности

- 0.001
0.851

- 0.003
0.744
0.931

-

На рис. 13.5 показано, что частотное распределение достаточно точно совпадает с плотностью нормального распределения вероятностей ( как и пред полагалось).

.

In [ 25 ] : plt figure ( figsize= ( 10, 6 ) )
log data = np log ( paths[ ~l] )
plt hist ( log_data, bins=70, normed= True,

_
.

.

label= ' Наблюдения
pit xlabel( ' Индекс ' )

.

1

,

color = ' b ' )

Статистический анализ

477

plt . ylabel ( ' Частота ' )
x = np . linspace ( plt . axis ( ) [ G ] , plt . axis ( ) [ l ] )
plt . plot ( x , scs . norm . pdf ( x , log _data . mean ( ) ,
log data . std ( ) ) , ' г ' , lw= 2 , 0 , label = ' HPB ' )
plt . legendQ ;

_

HPB
Наблюдения

2.00

1.75
1.50

1.25
td

н
о
H

я 1.00
F
0.75

0.50

0.25

0.00
3.75

4.00

4.25

4.50

4.75

5.00

5.25

5.50

Индекс

.

.

Рис 13.5 Гистограмма уровней логарифмического индекса в модели

геометрического броуновского движения и график плотности нормального
распределения вероятностей
На рис. 13.6 также можно увидеть доказательство гипотезы о нормальном
распределении уровней логарифмического индекса.

_

In [ 26 ] : sm . qqplot ( log data , Hne = ' s ' )
pit . xlabel ( ' Теоретически рассчитанные квантили ' )
pit . ylabel ( ' Выборочные квантили ' ) ;

Гипотеза о нормальном распределении

(

478

На предположении о нормальном распределении неизвестных значений доходности финансовых инструментов строится целый ряд
финансовых теорий. Python располагает эффективными статистическими и графическими средствами проверки того, подчиняются
ли данные временных рядов нормальному распределению.

Глава 13

5.50
Я

5.25

Я

н 5.00
и
га
CQ
4.75

0
нч
1-4

К 4.50
Я

о
о
1C 4.25
3
И
4.00
3.75

-4

0
-2
2
Теоретически рассчитанные квантили

4



Рис. 13.6. График “ квантиль квантиль” для уровней логарифмического
индекса, моделируемых геометрическим броуновским движением

Существующие исторические данные
В этом разделе мы проанализируем четыре финансовых временных ряда.
Два из них содержат котировки акций технологических компаний, а другие
два котировки биржевых инвестиционных фондов:








. —
MSFT . 0 —

APPL 0

SPY

GLD





Apple, Inc.;

Microsoft, Inc.;

SPDRS&P 500 Trust;
SPDR Gold Trust.

Для управления данными обратимся к программным инструментам би блиотеки pandas ( см. главу 8). На рис. 13.7 показано, как изменяются норма лизованные цены на финансовые инструменты во времени.
In [ 27 ] : import pandas as pd
In [ 28 ] : raw

=

_

pd . read csv ( ' . . / . . / source / tr _eikon _eod _data . csv ' ,
index _col =0 , parse dates = True ) . dropnaQ

_

Статистический анализ

479

In [29]: symbols

=
=

In [38]: data
data

= ['SPY ' , 'GLD',

1

AAPL.O'

' MSFT.O']

raw[symbols]
data.dropna()

In [31]: data.infoQ

Datetimelndex: 2138 entries, 2010 01 04 to 2018 06 29
Data columns (total 4 columns):
SPY
2138 non null float64
2138 non null float64
GLD
2138 non - null float64
AAPL.O
MSFT.O
2138 non null float64
dtypes: float64(4)
memory usage: 83.5 KB

- -

- -

-

In [32]: data.head()
Out[32]:
Date
2010-01-04
2010-01-05
2010 01-06
2010-01-07
2010-01-08

-

SPY

GLD

113.33
113.63
113.71
114.19
114.57

109.80
109.70
111.51
110.82
111.37

AAPL.O MSFT.O
30.572827
30.625684
30.138541
30.082827
30.282827

30.950
30.960
30.770
30.452
30.660

In [33]: (data / data.iloc[0] * 100).plot(figsize=(10, 6))

На рис. 13.8 приведены гистограммы изменения логарифмической доходности финансовых инструментов во времени.
In [34]: log returns = np.log(data / data.shift(l))
log returns.head()
Out[34]:
MSFT.O
AAPL.O
GLD
SPY

_
_

Date
2010-01-04
2010-01-05
2010 01-06
2010 01-07
2010-01-08

-

_

NaN
0.002644
0.000704
0.004212
0.003322

NaN

NaN

0.004951 0.006626 0.006807

In [35]: log returns.hist(bins=50, figsize=(l0, 8));

480

Глава 13

NaN

-0.000911 0.001727 0.000323
0.016365 - 0.016034 - 0.006156
0.006207
-0.001850 -0.010389
-

600



SPY

GLD
AAPL.O
MSFT.O

500

400

300

200

100

п?хЪ

ъ

п
Date

^

^

*6

TP4

TP

Puc. 13.7 . Изменение нормализованных цен на финансовые инструменты
во времени
AAPL .0

350

GLD

300
300
250

250

200

200

150

150

100

100

50

50

0

-0.10

-0.05

О

0.00

0.05

MSFT. O

-0.08 -0.06 -0.04 -0.02 0.00 0.02 0.04

SPY

400
350

300

300

250

250

200

200
150
150
100

100

50

50
О

-0.10

-0.05

О

0.00

0.05

0.10

-0.06 -0.04 -0.02

0.00

0.02

0.04

Рис. 13.8. Гистограммы логарифмической доходности финансовых инструментов
Статистический анализ

481

Теперь рассмотрим различные статистические показатели временных рядов. Заметьте, что во всех четырех случаях значение коэффициента эксцесса
не характерно для нормального распределения.
In [36]: for sym in symbols:
print('\пРезультаты для символа {}'.format(sym))
print(38 * ' ')
log data = np.array(log returns[sym].dropnaQ)
print statistics(log data) О

_

__

-

_

Результаты для символа SPY

Статистика

Значение

size
min
max
mean

2137.00000
0.06734
0.04545
0.00041
0.00933
0.52189
4.52432

std
skew
kurtosis

-

-

Результаты для символа GLD

Статистика

Значение

size

2137.00000
0.09191
0.04795
0.00004
0.01020
0.59934
5.68423

min
max
mean

std
skew
kurtosis

-

-

Результаты для символа AAPL.O
Статистика

Значение

size
min
max

2137.00000
0.13187
0.08502
0.00084
0.01591

mean
std

482

Глава 13

-

- 0.23510

skew
kurtosis

4.78964

Результаты для символа MSFT . O

Статистика

Значение

size
min

2137.00000
- 0.12103
0.09941
0.00054
0.01421
- 0.09117
7.29106

пах
mean

std
skew
kurtosis

О Статистические показатели временных рядов финансовых инструментов.



На рис. 13.9 приведен график “ квантиль
квантиль ” ( Q-Q) для тикера
,
SPY. Вполне очевидно что выборочные квантили не лежат на прямой линии, а
это указывает на то, что исходные данные не подчиняются нормальному распределению. Точки в левой и правой частях графика отдаляются от прямой
в область соответственно меньших и больших значений. Другими словами,
временные ряды характеризуются утяжеленными хвостами. Этот термин
пришел из статистики, где он обозначает ту часть распределения, в которой
наблюдаются сильные расхождения ( в отрицательную или положительную
сторону) с нормальным распределением. То же самое можно сказать и о графике для акций Microsoft ( рис. 13.10 ). На нем также наблюдаются утяжелен ные хвосты.

_

In [ 37 ] : sm . qqplot ( log returns[ ' SPY ' ] . dropnaQ , line = ' s ' )
plt . title ( ' SPY ' )
pit . xlabel ( ' Теоретически рассчитанные квантили ' )
pit . ylabel ( ' Выборочные квантили ' ) ;

_

In [ 38 ] : sm . qqplot ( log returns [ ' MSFT . O ' ] . dropnaQ , line = ' s ' )
pit . title ( ' MSFT . O ' )
pit . xlabel ( ' Теоретически рассчитанные квантили ' )
pit . ylabel ( ' Выборочные квантили ' ) ;

Статистический анализ

483

SPY
0.04

к
Я
К

0.02

H
Я

PQ

0.00

0

3

к
пн — 0.02
о
a

о
vo

Э

-0.04

CQ

- 0.06
-3

-2

-1

1

0

2

3

Теоретически рассчитанные квантили

.

Рис. 13.9 График “ квантиль — квантиль” для логарифмической
доходности акций SPY

MSFT.0
0.10

я

Я
Я

0.05

н

к

cd
PQ

W

0

0.00

3

к
я
о
а - 0.05
-Q

га
i

- 0.10

-3

-2

-1

1

0

2

3

Теоретически рассчитанные квантили

.

.

Рис 13.10 График “ квантиль — квантиль” для логарифмической
доходности акций MSFT 0

484

Глава 13

.

Наконец, можно выполнить статистические тесты.
In [ 39 ] : for sym in symbols :
print ( ' \ пРезультаты для символа {} ' . format ( sym ) )
print ( 32 * ' - ' )
log _data = np . array ( log returns[ sym ] . dropnaO )
normality tests ( log _data ) О

_

_

Результаты для символа SPY

Коэффициент асимметрии набора данных
Р - значение коэффициента асимметрии
Коэффициент эксцесса набора данных
Р - значение коэффициента эксцесса
Р - значение критерия нормальности

- 0.522
0.000
4.524
0.000
0.000

Результаты для символа GLD

Коэффициент асимметрии набора данных
Р - значение коэффициента асимметрии
Коэффициент эксцесса набора данных
Р - значение коэффициента эксцесса
Р - значение критерия нормальности

- 0.599
0.000
5.684
0.000
0.000

Результаты для символа AAPL . 0

Коэффициент асимметрии набора данных
Р - значение коэффициента асимметрии
Коэффициент эксцесса набора данных
P - значение коэффициента эксцесса
P - значение критерия нормальности

- 0.235
0.000
4.790
0.000
0.000

Результаты для символа MSFT . 0

Коэффициент асимметрии набора данных
P - значение коэффициента асимметрии
Коэффициент эксцесса набора данных
Р - значение коэффициента эксцесса
Р - значение критерия нормальности

- 0.091
0.085
7.291
0.000
0.000

О Проверка P-значений для временных рядов нескольких финансовых
инструментов.

Статистический анализ

485

P-значения во всех тестах равны нулю, а значит, гипотезу о нормальном
распределении временных рядов следует решительно отвергнуть. Это еще
раз показывает, что предположение о нормальном распределении доходностей акций и других классов активов ( как, например, в модели геометрического броуновского движения ) нельзя принимать безоговорочно. Во многих случаях приходится обращаться к более сложным моделям ( в частности, моделям
прыжковой диффузии или моделям со стохастической волатильностью ), способным генерировать утяжеленные хвосты.

Оптимизация портфеля



Портфельная теория Марковица
краеугольный камень финансового
анализа . В 1990 году Гарри Марковиц получил Нобелевскую премию по экономике за ее теоретическое обоснование. Несмотря на то что портфельная
теория была сформулирована еще в 1950-х годах, ее до сих пор преподают
студентам финансовыхспециальностей, и она находит широкое практическое
применение ( обычно с теми или иными модификациями) 3. В этом разделе мы
рассмотрим основные положения теории.
Введение в портфельную теорию изложено в главе 5 книги Коупленда,
Уэстона и Шастри [ 3]. Как указывалось ранее, ключевым постулатом теории
является предположение о нормальном распределении значений доходности.
Рассматривая только среднее значение и дисперсию, мы неизбежно предполагаем, что для математического описания распределения капитала на конец периода не понадобятся никакие другие статистические показатели. Если только
инвесторы не располагают специальной функцией полезности (а именно квадратичной ) , следует предполагать, что доходность имеет нормальное распределение, которое можно целиком описать с помощью среднего и дисперсии.

Данные
В последующих примерах будут использоваться те же финансовые инструменты, что и ранее. Основная идея портфельной теории Марковица заключается в диверсификации с целью достижения минимального портфельного
риска при заданном уровне целевой доходности или максимальной доходности портфеля при заданном уровне риска. Можно ожидать, что положительный эффект от такой диверсификации будет достигаться при большом числе разнообразных активов. Но в нашем случае для демонстрации ключевых
3

См. работу Марковица [ 9].

486

Глава 13

принципов будет достаточно четырех финансовых инструментов. Частотное
распределение их логарифмических доходностей показано на рис. 13.11.

' MSFT . O ' , ' SPY ' , ' GLD ' ] О

In [ 40 ] : symbols = [ ' AAPL . O ' ,
In [ 41] : noa = len ( symbols )

©

In [ 42 ] : data = raw [ symbols ]

In [ 43 ] : rets = np . log ( data

/ data . shift (l) )

In [ 44 ] : rets . hist ( bins= 40 , figsize= ( 10 , 8 ) ) ;

О Четыре финансовых инструмента в структуре портфеля.
Количество финансовых инструментов.

Q

AAPL.0

GLD

400
400
350
350
300

300

250

250

200

200

150

150

100

100

50

50
О

0
-0.10

-0.05

0.00

-0.08 -0.06 -0.04 -0.02 0.00

0.05

MSFT.O

0.02 0.04

SPY
400

400

350
300

300

250
200

200

150
100

100

50
О

0
-0.10

-0.05

.

0.00

0.05

0.10

-0.06 -0.04 -0.02

0.00

0.02

0.04

Рис. 13.11 Гистограммы логарифмической доходности выбранных

финансовых инструментов

Статистический анализ

487

Процесс выбора портфеля основывается на изучении ковариационной
матрицы инвестиционных финансовых инструментов. Библиотека pandas
располагает встроенным методом генерирования ковариационной матрицы,
к которой можно применить тот же самый коэффициент масштабирования.
In [ 45 ]: rets.mean() * 252 О
Out [ 45 ]: AAPL.O
0.212359
MSFT.O
0.136648
0.102928
SPY
0.009141
GLD
dtype: float64

In [ 46 ] : rets.covQ * 252
Out [ 46 ] :

.

AAPL O
MSFT O
SPY
GLD

.

©

.

.

AAPL O

MSFT O

SPY

GLD

0.063773
0.023427
0.021039
0.001513

0.023427
0.050917
0.022244
- 0.000347

0.021039
0.022244
0.021939
0.000062

0.001513
- 0.000347
0.000062
0.026209

О Среднегодовая доходность.
@

Годовая ковариационная матрица.

Теоретическое обоснование
В дальнейшем предполагается, что инвестору не разрешается открывать
короткие позиции по финансовому инструменту. Разрешены только длинные
позиции, а это значит, 100% инвестиционного капитала нужно распределить
между доступными инструментами так, чтобы все позиции были длинными
(положительными) и в сумме составляли 100%. Располагая четырьмя инструментами, можно, например, инвестировать в них равные суммы: по 25% до ступного капитала на каждый. В следующем фрагменте генерируются четыре
случайных числа из равномерного распределения в диапазоне от 0 до 1, после
чего значения нормализуются так, чтобы их сумма равнялась 1.

In [ 47 ] : weights = np.random.random( noa) О
weights /= np.sun(weights) ©

In [ 48 ] : weights
Out [ 48 ]: array([0.07650728, 0.06021919, 0.63364218, 0.22963135])
In [ 49 ] : weights.sun()
Out [ 49 ]: 1.0
488

Глава 13

О Случайные веса инструментов в портфеле...
Q

...нормализуемые в диапазоне от 1 до 100%.
Как указывалось выше, сумма весов должна равняться 1, т.е.





/

^

и>. = 1, где

I количество финансовых инструментов, a w . > 0 вес финансового инструмента i. В уравнении 13.1 приведена формула ожидаемой доходности портфеля, учитывающая веса всех финансовых инструментов. Доходность счи тается ожидаемой в том смысле, что историческая средняя доходность здесь
рассматривается как наилучшая оценка будущей доходности. В этой формуле
г. зависящие от состояния портфеля будущие доходности ( вектор значений
доходности, для которых предполагается нормальное распределение), а р
ожидаемая доходность инструмента i. Наконец, w 1 это транспонированный
вектор весов, а р вектор ожидаемых доходностей ценных бумаг.









Уравнение 13.1. Общая формула ожидаемой доходности портфеля

С, = Е



=

>

= 2 ,ЕМ =
/

= £ wiH =
I

=

W

т

/и.

В переводе на Python такая формула представляется единственной ин струкцией ( включающей пересчет в годовом выражении ).
In [ 50 ] : np . sum ( rets . meanQ * weights ) * 252
Out [ 50 ] : 0.09179459482057793

О

О Годовая доходность портфеля с учетом весов финансовых инструментов.



Другой важный элемент портфельной теории Марковица дисперсия ожидаемой доходности портфеля. Если ковариация между двумя ценными бумато дисперсию отдельной
гами определяется как сг . = •'•

•‘
* •
••

-8


• Л• •

-10

-12

- ю. о

-7.5

-5.0

-2.5

2.5

0.0

5.0

7.5

Рис. 13.24. Тестовые данные для алгоритма кластеризации

Кластеризация методом к-средних



Одно из преимуществ библиотеки Scikit -learn
наличие стандартизированного программного интерфейса для применения различных алгоритмов.
Приведенный ниже код выполняет базовые действия для кластеризации методом -средних, которые затем повторяются для других моделей:





520

^

импорт класса модели;
инициализация объекта модели;

обучение модели на тестовых данных;
прогнозирование результатов в рамках обученной модели.
Глава 13

Результаты показаны на рис. 13.25.


• ••
• •V *ii:«*



<

ч •
'AVI

••••

• •
4w



-10.0

•••* N••
/




#




-7.5

-5.0

-2.5

(



0.0

2.5

5.0

7.5

.

Рис. 13.25 Тестовые данные и распознанные кластеры
In [ 6 ] : from sklearn. cluster import KMeans

О

In [ 7 ] : model = KMeans ( n_clusters=4, random_state=0)

©

.

In [ 8 ] : model fit ( X ) ©
Out [ 8 ] : KMeans ( algorithm= ' auto ' , copy _ x=True, init= ' k - means++ ' ,
max iter =30O, n _clusters=4, n_init=10, n jobs=None,
precompute distances= ' auto ' , random_ state=0,
tol=0.0001, verbose=0 )

_

_

_

_

.

In [ 9 ] : y kmeans = model predict ( X )

О

In [ 10 ] : y _ kmeans [ : 12 ] ©
Out [ 10 ] : аггау ( [ 1, 1, 0, 3, 0, 1, 3, 3, 3 , 0, 2 , 2 ] , dtype=int 32 )

.
.

In [ 11] : plt figure( figsize= ( 10, 6 ) )
plt scatter ( X [ :, 0 ] , X [ : , 1], c =y _kmeans , cmap^ coolwarm 1 );

О Импорт класса модели из библиотеки Scikit -learn.

© Инициализация объекта модели; значения параметров подобраны
в соответствии с особенностями тестового набора данных.

Статистический анализ

521

© Обучение объекта модели на необработанных данных.
О Прогнозирование ( номеров ) кластеров в необработанных данных.

© Вывод спрогнозированных номеров кластеров.

Смесь гауссиан
В качестве альтернативы методу кластеризации рассмотрим смесь гаусси ан. Порядок действий здесь тот же самый, и при аналогичной параметризации
результаты будут теми же.
In [12]: from sklearn.mixture import GaussianMixture
In [13]: model

=

_

_

GaussianMixture ( n components = 4 , random state = 0 )

In [14]: model . fit ( X )
0ut [ 14 ] : GaussianMixtureCcovariance-type full ' ,
init _ params = ' kmeans ' ,
max _ iter =100 , means_ init = None ,
n components= 4 , n Lnit = l ,
precisions tnit = None , random _state = 0 ,
reg _covar = le - 06 , tol =0.001 , verbose = 0 ,
verbose interval = 10 , warm start = False ,
weights init = None )

^

_

_

_
_

In [15]: y _gm

_

_

= model . predict ( X )

In [ 16 ] : y _gm[ : 12 ]
0ut [ 16 ] : array ( [ 1 , 1 , 0 , 3 , 0 , 1 , 3 , 3 , 3 , 0 , 2 , 2 ] )

_

In [17]: ( y gm
Out[17]: True

==

_

y kmeans ) . all ( )

©

О Результаты применения метода fc- средних и смеси гауссиан оказываются
одинаковыми.

Обучение с учителем
Обучение с учителем представляет собой концепцию машинного обучения,
согласно которой система получает определенные подсказки в виде известных
результатов или наблюдаемых данных. Это означает, что необработанные дан ные уже содержат то, чему алгоритм машинного обучения должен обучиться.

522

Глава 13

В последующих примерах мы сконцентрируемся на рассмотрении задач клас сификации, которые противопоставляются задачам оценивания. Последние
предполагают аппроксимацию реальных значений, тогда как в задачах классификации делается попытка отнести ту или иную комбинацию признаков к
определенному классу ( представленному целочисленным значением), входя щему в сравнительно небольшой набор классов.
В предыдущем разделе мы увидели, что алгоритмы обучения без учителя формируют собственные метки категорий для выявленных кластеров. В
случае четырех кластеров метками становятся числа 1, 2, 3 и 4. В обучении с
учителем такие метки предоставляются заранее, что позволяет алгоритму обучаться зависимостям между отдельными признаками и категориями ( классами ) данных. Другими словами, на этапе обучения модели алгоритм уже знает правильный класс для заданной комбинации признаков.
В этом разделе мы рассмотрим применение следующих алгоритмов классификации: гауссовский наивный байесовский классификатор, логистическая регрессия, деревья принятия решений, глубокие нейронные сети и метод
опорных векторов10.

Данные
Библиотека Scikit -learn также позволяет создавать тестовые выборки для
алгоритмов классификации. Чтобы иметь возможность визуализировать результаты, тестовый набор должен включать два вещественных информативных признака и бинарную метку (0 или 1). В следующем коде создается тестовый набор, выводится фрагмент набора и строится диаграмма ( рис. 13.26).
In [ 18 ] : from sklearn . datasets import make_classification

_

In [ 19 ] : n sampl.es
In [ 20 ] : X , у

In [ 21 ] : X[ : S ]

10

=

108

= make _classification ( n_ samples = n _ samples , n _features = 2 ,
n _ informative = 2 , n _ redundant =0 ,
n _ repeated = 0 , random_ state = 258 )
О

Более подробно все алгоритмы обучения с учителем, поддерживаемые библиотекой Scikitlearn, описаны в официальной документации (https : / / scikit - learn . огд / stable /
supervised learning . html ). Многие из этих алгоритмов применяются не только в задачах
классификации, но и в задачах оценивания.

_

Статистический анализ

523

Out [ 21] : аггау ( [ [ 1.6876 ,
[ - 0.4312 ,
[ - 1.4393 ,
[ 1.118 ,
[ 0.0502 ,
In [ 22 ] : X . shape
0ut [ 22 ] : ( 100 , 2 )

- 0.7976 ] ,
- 0.7606 ] ,
- 1.2363 ] ,
- 1.8682 ] ,
0.659 ]] )

О

In [ 23 ] : у [ : 5 ] ©
Out [ 23 ] : array ( [ l , 0 , 0 , 1 , 1 ] )

©

In [ 2.4 ] : y . shape
Out [ 24 ] : ( 100 , )

In [ 25 ] : plt . figure ( figsize = ( 10 , 6 ) )
plt . scatter ( x = X[ : , 0 ] , y = X[ : , 1 ] , c = y , crnap = ' coolwam ' ) ;

О

Пары вещественных признаков .

©

Бинарная метка.






.
• ' ••••• , “
•• •






•••



•ч •
.
•.

*
4
>

^

11

%

4



'

-*



%



1 Г




• ••




ф



ф

-3

-2

-1

0

1

2

Рис. 13.26. Тестовые данные для алгоритма классификации

524

Глава 13

3

Гауссовский наивный байесовский классификатор
Гауссовский наивный байесовский классификатор ( Gaussian Naive Bayes —
GNB) считается хорошим базовым алгоритмом для широкого круга задач
классификации. Порядок действий здесь такой же, как и в методе /с-средних.

sklearn.naivej&ayes import GaussianNB
from sklearn.metrics import accuracy _ score

In [26]: from

In [27]: model

=

GaussianNBQ

In [28]: model . fit ( X , y )
Out [ 28 ] : GaussianNB ( priors = None , var smoothing = le - 09 )

_

In [29]: model . predict _ proba ( X ) . round ( 4 ) [:5]
0ut[29]: array ( [[ 0.0041 , 0.9959 ] ,
[ 0.8534 , 0.1466 ] ,
[ 0.9947 , 0.0053 ] ,
[ 0.0182 , 0.9818 ] ,
[ 0.5156 , 0.4844 ] ] )
In [30]: pred

О

= model . predict ( X ) ©

In [31]: pred ©
0ut [ 31 ] : array ( [ l , 0 , 0 ,
1, 0 , 1 ,
О • о • о,
0, 0, 1,
1, 0, ©,
0, 0, 1,
ч

ч

In [ 32 ] : pred == у ©
Out [ 32 ] : array ( [ True ,
True ,
True ,
True ,
True ,
True ,
True ,
True ,
True ,
True ,
True ,
True ,

1,
1,
1,
1,
1,
0,

0, 0 ,
0, 0,
1, 0 ,
1, 1,

1,
0,
1,
1,
1, 1, 1,
1, 1, 1,

1,
1,
0,
0,
1,
1,

1,
0,
0,
0,
1,

0,
0,
0,
0,
0,
1, 0,

0, 0, 0,
0, 0, 0,
0 , 1, 1 ,
1 , 1 , 1,
0 , 1, 0 ,
0 , 0, 0 ,

0,
1,
1,
1,
0,
0,

True , True , False , True ,
True , False , True , True ,
True , True , True , True ,
False , False , False , True , True ,
True , True , True , True , False ,
True , True , True , True , True ,
True , True , True , True , True ,
True , False , True , False , True ,
True , True , True , True , True ,
True , False , True , True , True ,
True , True , True , True , True ,
False , True , False , True , True ,
True ,
True ,
True ,

1, 1,
0, 1,
0, 0,
0, 0,
0, 1,
0])

True ,
True ,
True ,
True ,
True ,
True ,
True ,
True ,
True ,
True ,
True ,
True ,

Статистический анализ

525

True , True , True , True ,
True , False , True , False ,
True , True ] )
In [ 33 ] : accuracy _score ( y , pred )
Out[ 33 ] : 0.87

True ,
True ,

True ,
True ,

True ,
True ,

О

О Вывод вероятностей, присваиваемых каждому классу после обучения модели.

© Прогнозирование бинарных классов для набора данных на основе полученных вероятностей.

© Сравнение предсказанных классов с действительными.
© Оценка точности прогнозируемых значений.
На рис. 13.27 визуализированы правильные и ложные прогнозы, получен ные классификатором GNB.



.
• ' ••••
• •
••• •





i>

>



%

ч .
. ^ •

%

1t*


• «

1

-2

#

«



х

X



к



• •



• •


-3

•• •


X

х

-1

0

1

2


3

Рис. 13.27. Правильные ( точки ) и ложные ( крестики ) прогнозы,
полученные классификатором GNB
In [ 34 ] : Хс

Xf

= Х[ у ==
= Х[ у ! =

pred ]
pred ]

О
©

In [ 35 ] : plt . figure ( figsize = ( 10 , 6 ) )

526

Глава 13

.

plt scatter ( x=Xc [ :, б ],
marker = ' o ' ,
plt scatter ( x=Xf [ :, 0 ] ,
marker = ' x ' ,

.

y=Xc [ :, 1] , c =y [ y == pred ],
cmap= ' coolwarm ' ) О
y=Xf [ :, 1], c =y [ y ! = pred ] ,
cmap= ' coolwarm ' ) ©

О Выбор и отображение правильных прогнозов.

© Выбор и отображение ложных прогнозов.
Логистическая регрессия



это быстрый и хорошо масштабируемый алгоритм классификации. Точность в данном конкретном случае получается немного выше, чем у классификатора GNB.
Логистическая регрессия

.

In [ 36 ] : from sklearn linear model import LogisticRegression



In [ 37 ] : model = LogisticRegression ( C =l, solver = ' lbfgs ' )

.

In [ 38 ] : model fit ( X , y )
Out [ 38 ] : LogisticRegression ( C=l, class weight=None, dual=False,
fit intercept=True, intercept scaling=l,
max iter =100, multi_class= ' warn ' ,
n jobs =None , penalty= 1121 ,
random_ state=None, solver = ' Ibfgs ' ,
tol=0.0001, verbose=0, warm start=False )

_

_
_

_

_

_

_

.

.

In [ 39 ] : model predict proba ( X ) round ( 4 ) [ : 5 ]
0ut [ 39 ] : array ( [ [ 0.011 , 0.989 ] ,
[ 0.7266, 0.2734 ] ,
[ 0.971 , 0.029 ] ,
[ 0.04 , 0.96 ] ,
[ 0.4843, 0.5157 ] ] )

.

In [ 48 ] : pred = model predict ( X )

_

In [ 41] : accuracy score ( y, pred )
0ut [ 41] : 0.9

In [ 42 ] : Xc = X [ y == pred]
Xf = X [ y ! = pred ]

.
.

In [ 43 ] : plt figure( figsize= ( 19, 6 ) )
plt scatter ( x=Xc [ :, 0 ] , y=Xc [ :, 1] , c =y [ y == pred] ,
marker = ' o ' , cmap= ' coolwarm ' )

Статистический анализ

527

plt . scatter ( x = Xf [ : , G ] , y= Xf [ : , 1 ] , c = y [ y ! = pred ] ,
marker = ' x ' , cmap= ' coolwarm ' ) ;

Дерево решений



еще один хорошо масштабируемый алгоритм класси Дерево решений
фикации. При максимальной глубине 1 он уже обеспечивает немного более
высокую точность, чем наивный байесовский классификатор и логистическая
регрессия ( рис. 13.28 ).
In [44]: from

sklearn.tree import DecisionTreeClassifier

= DecisionTreeClassifier ( max_depth = l )

In [45]: model

In [46]: model . f i t ( X , y )
Out [ 46 ] : DecisionTreeClassifier ( class weight = None , criterion = ' gini ' ,
max depth = l , max_features = None ,

_

_

max _ leaf _nodes= None ,
min _ tmpurity _decrease =0.0 ,
min _ impurity _split = None ,
min _samples _ leaf =1 ,
min _ samples _ split = 2 ,
min _weight_fraction _ leaf =0.0 ,
presort = False , random_ state = None ,
splitter = ' best ' )

In [ 47 ] : model . predlct
0ut [ 47 ] : array ( [[ 0.08 ,
[ 0.92 ,
[ 0.92 ,
[ 0.08 ,
[ 0.08 ,
In [48]: pred

_proba ( X ) . round ( 4 ) [ : 5 ]
0.92 ] ,
0.08 ] ,
0.08 ] ,
0.92 ] ,
0.92 ]] )

= model . predict ( X )

In [49]: accuracy _ score ( y , pred )
Out[49]: 0.92

= X[ y == pred ]
Xf = X[ y ! = pred ]

In [58]: Xc

In [ 51 ] : plt . figure ( figsize = ( 10 , 6 ) )

528

Глава 13

.
.

plt scatter ( x=Xc [ : , б ],
marker = ' o ' ,
plt scatter ( x = Xf [ : , 0 ],
rnarker = ' x ' ,

y =Xc [ :, 1], c=y [ y == pred ] ,
cmap = ' coolwarm ' )
y= Xf [ :, 1] , c= y [ y ! = pred] ,
,
crnap= coolwarn ' );



.• •• • •



.

Л







• > ••
х
••• •
*
. ••


•Vх
«

• * х



/
^
*
• •
%

• •
• •

.

X

1

Ф



Л

9
-3

-1

-2

1

0

2

3

Puc. 13.28. Правильные ( точки ) и ложные ( крестики ) прогнозы , полученные
с помощью дерева решений ( nax _ depth=l )

Если увеличить максимальную глубину дерева решений, то можно получить идеальные результаты.

.

In [ 52 ] : print ( ' { : >8 s } | { : 8 s } ' fornat ( ' Глубина ' , ' Точность ' ) )
print ( 28 * ' - ' )
for depth in range(l, 7 ) :
model = DecisionTreeClassifier ( max depth=depth )
model f l t ( X, y )
acc = accuracy _ score ( y , model predict ( X ) )
print ( ' { : 8d) | { : 8.2 f } ' format ( depth, acc ) )
Глубина | Точность

_

.

.

1
2

3
4
5
6

.

0.92
0.92
0.94
0.97
0.99
1.00

Статистический анализ

529

Глубокие нейронные сети



Глубокие нейронные сети ( Deep Neural Networks DNN ) считаются одним
из самых мощных, хотя и вычислительно затратных, алгоритмов прогнози рования и классификации. Своей популярностью они во многом обязаны открытому пакету TensorFlow компании Google и другим технологическим достижениям последних лет. DNN способны обучаться и моделировать сложные
нелинейные взаимосвязи. Несмотря на то что нейронные сети появились еще
в 1970-е годы, их полномасштабная практическая реализация стала возможной только с появлением высокопроизводительных процессоров ( CPU, GPU,
TPU ), эффективных численных алгоритмов и соответствующего программного обеспечения.
Другие алгоритмы машинного обучения, в частности линейные модели логистической регрессии, способны эффективно обучаться при решении стан дартных задач оптимизации, однако DNN основаны на глубоком обучении, что
требует многократного повторения одних и тех же этапов настройки определенных параметров ( весов) и сравнения полученных результатов с исходными
данными. В этом смысле глубокое обучение можно сравнить с применением
метода Монте- Карло в финансовой математике, когда, к примеру, для оцен ки европейского колл -опциона необходимо смоделировать 100 000 траекторий базового актива. С другой стороны, формула оценки опционов Блэка
Шоулза Мертона существует в замкнутой форме и может быть вычислена
аналитическим способом.
Высокая гибкость и вычислительная эффективность метода Монте - Карло
достигается за счет повышенных требований к оборудованию и оперативной
памяти. То же самое справедливо и в отношении глубокого обучения, которое
намного гибче других алгоритмов машинного обучения, но для этого требуется больше вычислительных ресурсов.





DNN и библиотека Scikit-learn
Несмотря на иное предназначение, библиотека Scikit-learn реализует такой
же программный интерфейс для класса алгоритмов MLPClassifier 11 ( модель
DNN, классификатор многослойного перцептрона ), как и для других алгорит-

мов машинного обучения . Располагая всего двумя так называемыми скрытыми слоями, эта модель позволяет достичь оптимального результата на тестовых данных. ( Скрытые слои то, что отличает глубокое обучение от простого
машинного обучения. В этой модели веса “ обучаются ”, например в контексте



11

Подробное описание класса доступно в документации (https: / / scikit - learn . огд / stable /
modules / generated / sklearn . neural network . MLPClassifier . html ) .

530

_

Глава 13

линейной регрессии, а не вычисляются напрямую с помощью регрессии по
методу наименьших квадратов . )
In [ 53 ] : from sklearn . neuraljrietwork import MLPClassifier

In [ 54 ] : model

= MLPClassifier ( solver = ' lbfgs ' , alpha = le - 5 ,
hidden _ layer _sizes= 2 * [ 75 ] ,
random_ state = l 0 )

In [ 55 ] : % time model . fit ( X , y )
CPU times : user 537 ms , sys : 14.2 ms , total : 551 ms
Wall time : 340 ms
Out [ 55 ] : MLPClassifier ( activation = ' relu ' , alpha = le - 05 ,
batch _size = ' auto ' , beta _ l =0.9 ,
beta _ 2= 0.999 , early_ stopping = False ,
epsilon = le - 08 , hidden layer sizes =[ 75 , 75 ] ,
learning rate = ' constant ' ,
learning rate init = 0.001 , max iter = 200 ,
momentum =0.9 , n iter _ no change = 10 ,
nesterovs momentum=True , power t =0.5 ,
random_ state= 10 , shuffle = True ,
solver = ' Ibfgs ' , tol = 0.0001 ,
validation_fraction =0.1 , verbose = False ,
warm start = False )

_

_
_

_

_

_

_

_

_

_

_

In [ 56 ] : pred = model
pred
0ut [ 56 ] : array ( [ l , 0 ,
1, 0,
0, 0,
1, 0,
1, 0 ,
0, 0,

predict ( X )
0,
1,
0,
0,
0,

1,
1,
1,
1,
1,
1, 0,

1,
0,
1,
1,
1,
1,

1,
1,
1,
1,
1, 1,
1, 1,

0,
1,
0,
1,

1,
0,
0,
0,
1,
0,

1,
0,
0,
0,
1,
1,

0,
0,
0,
0,
0,
1,

1,
0,
0,
1,
1,
0,

0,
0,
1,
1,
1,
0,

0,
0,
1,
1,
1,
0,

0,
1,
1,
1,
0,

1, 1 ,
0, 1,
0, 0,
1, 0 ,
0, 1,
0, 0] )

0,
0,
1,
0,
0,

_

In [ 57 ] : accuracy score ( y , pred )
Out [ 57 ] : 1.0

DNN и библиотека TensorFlow
Программный интерфейс библиотеки TensorFlow отличается от стандарта,
принятого в Scikit -learn. Тем не менее схема применения класса DNNClassifier
такая же простая .
In [ 58 ] : import tensorfiow as tf
tf . logging . set _verbosity ( tf . logging . ERROR )

О

Статистический анализ

531

In [59]: fc = [tf.contrib.layers.real_ valued _ column( ' features')]

©

In [68]: model = tf.contrib.learn.DNNClassifier( hidden _ units= 5 * [258],
n _classes= 2,
feature _ columns=fc) ©
In [61]: def input _ fn():

0

fc = {' features ': tf.constant(X)}
la = tf.constant(y)
return fc, la
In [62]: %time model.fit(input _fn =input _fn, steps=108) ©
CPU times: user 7.1 s, sys: 1.35 s, total: 8.45 s
Wall time: 4.71 s
Out[62]: DNNClassifier( params ={' head ': < tensorflow.contrib.learn ,
python.learn ... head._ BinaryLogisticHead object at
0xla3ee692 bO >, ' hidden _ units': [250, 250, 250, 250, 250],
' feature_columns': (_ RealValuedColumn(column _ name= 'features ',
dimensions, default _ value= None, dtype=tf.float32,
normalizer = None),), ' optimizer ': None, ' activation _fn ':
, 'dropout ': None,
'gradient _ clip _ norm ': None, 'embedding _lr _ multipliers': None,
'input _ layer _ min _ slice _ size': None})
In [63]: model.evaluate(input _ fn =input _fn, steps= l) ©
Out[63]: {'loss': 0.18724777,
' accuracy ': 0.91,
'labels / prediction _ mean ': 0.5003989,
'labels/actual _ label _ mean ': 0.5,
' accuracy / baseline _ label_ mean ': 0.5,
' auc': 0.9782,
' auc _ precision _ recall': 0.97817385,
' accuracy/threshold _0.500O00_ mean ': 0.91,
' precision/ positive _ threshold _ O.5O0OO0_ mean ': 0.9019608,
' recall/ positive _ threshold _0.5OO00O _ mean ': 0.92,
' global _ step': 100}
In [64]: pred = np.array(list(model.predict(input _fn =input _fn)))@
pred[:10]©
Out[64]: array([l, 0, 0, 1, 1, 0, 1, 1, 1, 1])
In [65]: %time model.fit(input _fn =input _fn, steps=750)©
532

Глава 13

CPU tines : user 29.8 s, sys: 7.51 s, total: 37.3 s
Wall tine: 13.6 s
,
0ut [ 65 ] : DNNClassifier ( parans={ head ' : ctensorflow contrib learn ,
python learn
head BinaryLogisticHead object at
0 xla3 ee692b0>, 1 hidden_units ' : [ 250, 250, 250, 250, 250 ] ,
' feature_colunns ' : ( _RealValuedColunn( colunn nane=
' features ' , dinension=l, default value=None, dtype=
tf float 32, nornalizer =None ) ,) , ' optinizer ' : None,
' activation_ fn ' : < function relu at Oxla 3 aa 75 b70>,
' dropout ' : None, ' gradient _clip_norn ' : None,
' enbedding lr nultipliers ' : None,
' input layer nin_ slice size ' : None } )

.

_

.

_

_ _
_

.

.

._

.. .

_

_

_

.

_

In [ 66 ] : nodel evaluate ( input fn=input fn, steps=l) ©
Out [ 66 ] : { ' loss ' : 0.09271307,
' accuracy ' : 0.94,
' labels / prediction nean ' : 0.5274486,
' labels / actual label_nean ' : 0.5,
' accuracy / baseline_label_nean ' : 0.5,
' a u c ' : 0.99759996,
1
auc _precision recall ' : 0.9977609,
' accuracy / threshold_0.5O 0000 nean ' : 0.94,
' precision / positive_ threshold_0.500000_nean ' : 0.9074074,
' recall / positive_ threshold_0.5OOO00_nean ' : 0.98,
' global_ step ' : 850}

_

_

_

_

О Задание уровня детализации для журнала TensorFlow.

© Абстрактное определение вещественных признаков.
© Создание объекта модели.
0 Данные признаков и меток вводятся с помощью функции.

© Обучение и оценка модели.
© Прогнозирование меток на основе признаков.
© Повторное обучение модели с увеличением числа шагов; предыдущие
результаты служат отправной точкой.

© В результате повторного обучения точность модели повысилась.
Этот пример показывает лишь малую толику возможностей библиотеки TensorFlow, которая применяется во множестве ответственных проектов,
как, например, инициатива компании Alphabet по разработке беспилотных
Статистический анализ

533

автомобилей. Что касается скорости обучения моделей TensorFlow, то основ ной выигрыш достигается за счет использования специализированного оборудования ( в первую очередь GPU и TPU вместо CPU ) .

Преобразование признаков
Преобразование вещественных признаков может требоваться по целому
ряду причин. В следующем примере показаны типичные варианты преобра зований. Результаты сравниваются на рис. 13.29.
In [67]: from sklearn inport preprocessing
In [68]: X[:5]
0 ut [ 68 ]: array([[ 1.6876,
[ - 0.4312,
[ - 1.4393,
[ 1.118 ,
[ 0.0502,

- 0.7976],
- 0.7606],
- 1.2363],
- 1.8682],
0.659 ]])

In [ 69 ] : Xs = preprocessing .StandardScalerQ .fit _transforn(X)
Xs[:5]
0 ut [ 69 ]: array([[ 1.2881, - 0.5489],

[- 0.3384,
[- 1.1122,
[ 0.8509,
[ 0.0312,

О

- 0.5216],

- 0.873 ],
- 1.3399],
0.5273]])

In [70]: Xn = preprocessing . MinMaxScaler().fit _ transform(X)
Xn[:5]
Out [ 70 ]: array([[0.7262, 0.3563],
[0.3939, 0.3613],
[0.2358, 0.2973],
[0.6369, 0.2122],
[0.4694, 0.5523]])

©

In [71]: Xnl = preprocessing.Nomalizer(nom ='ll ' ).transform(X)
Xnl[:5]
Out[71]: array([[ 0.6791, - 0.3209],
[ - 0.3618, - 0.6382],
[ - 0.5379, - 0.4621],
[ 0.3744, - 0.6256],
[ 0.0708, 0.9292]])

©

In [72]: Xn 2 = preprocessing.Nomalizer(nom ='121 ).transform(X)

©

534

Глава 13

Xn2[ :5 ]
Out [ 72 ] : array ( [ [ 0.9041,
[ - 0.4932 ,
[ - 0.7586 ,
[ 0.5135 ,
[ 0.076 ,

- 0.4273 ] ,
- 0.8699 ] ,

- 0.6516 ] ,
- 0.8581] ,
0.9971] ] )

.

In [ 73 ] : plt figure ( figsize= ( 10, 6 ) )
markers = [ ' o ' ,
' x ', ' л', ' v ' ]
data sets = [ X, Xs, Xm, Xnl, Xn 2 ]
labels = [ ' Необработанные данные ' , ' Стандартное норм
распределение ' , ' Минимакс ' , ' Норма L1 ' , ' Норма L 2 ' ]
for х, m, l in zip( data _sets, markers, labels ) :
plt scatter ( x=x [ :, 0 ] , y =x [ :, 1], c=y ,
marker =m, cmap= ' coolwarm ' , label=l)

_

.

.

.

plt legend( );

О Приведение признаков к стандартному нормальному распределению с
нулевым средним и единичной дисперсией.

© Приведение каждого признака к требуемому диапазону, определяемому
минимальным и максимальным значениями.

© Индивидуальное масштабирование признаков в соответствии с векторной нормой L1 или L2.

4

••

3

X
д
Т

Необработанные данные
Стандартное норм, распределение
Минимакс
Норма L1
Норма L2

2

1

**

* *

•*

*4



*

«я? ' .г


О

-1



лУ
•%

-2

-3

-3

-2

.

-1

О

1

2

3

.

Рис 13.29 Сравнение исходных и преобразованных данных
Статистический анализ

535

В задачах распознавания образов преобразование данных в формат категориальных признаков часто помогает получить приемлемые результаты.
Для этого вещественные значения признаков транслируются в ограниченное,
фиксированное количество возможных целочисленных значений ( категорий,
классов ).

In [74]: Х[:5]
0u t[ 7 4 ] : а г г а у ( [[ 1 . 6 8 7 6 ,
[ - 0.4312,
[- 1.4393,
[ 1.118 ,
[ 0.0502,

In [75]: Xb

=

-0.7976],
-0.7606],
- 1 . 2 3 6 3],
- 1 . 8 6 8 2] ,
0 . 6 5 9 ]] )

_

preprocessing.BinarizerQ .fit transform(X)

ХЬ[:5]

0u t [ 7 S ] : а г г а у ( [ [ 1 . ,
[0 . ,
[0 . ,
[1 . ,
[1 . ,

In [76]: 2 ** 2
Out[76]: 4

0. ],
0 . ],
0. ]»
0. ],
!• ] ] )

©

In [ 7 7 ]: Xd = np.digitize(X, bins=[- l, 0, 1])
Xd[:5]
0u t [ 7 7 ] : а г г а у ( [ [ 3 ,
[1,
[0,
[3,
[ 2,

In [78]: 4 ** 2
0u t[ 7 8 ] : 1 6

О

©

1] ,
1]
0] ,
0] ,
2 ]] )

.

©

О Преобразование признаков в бинарные категории.
© Количество возможных комбинаций значений у двух бинарных признаков.
© Преобразование признаков в категориальные признаки на основе списка
значений, применяемых при сортировке.

© Количество возможных комбинаций значений при сортировке двух признаков по трем значениям.

536

Глава 13

Разделение обучающих и тестовых наборов: метод опорных векторов
На данном этапе у любого опытного разработчика систем машинного обучения наверняка возникли вопросы по поводу приведенных реализаций:
во всех примерах одни и те же данные использовались как для обучения, так
и для прогнозирования. Конечно же, работу алгоритма машинного обучения лучше всего оценивать, используя для обучения и тестирования разные
( под ) наборы данных. Это приближает нас к реальным сценариям.
В библиотеке Scikit -learn имеется удобная функция train_ test _ sp lit ( ) ,
которая позволяет случайным ( но воспроизводимым ) образом разделять на боры данных на обучающие ( тренировочные) и тестовые. В следующем примере применяется еще один алгоритм классификации: метод опорных векторов
(Support Vector Machine
SVM ). Сначала модель обучается на тренировоч ном наборе.
"



sklearn.svm import SVC
from sklearn.model selection import train _ test _ split

In [79]: from

_

_

_

_

_

In [ SO ] : train x , test x , train _y , test_ y = train test split ( X , y ,
test _size = 0.33 , random state =0 )
In [ SI ] : model

=

_

SVC ( C =1 , kernel = ' linear ' )

In [ 82 ] : model . fit ( train _ x , train _ y ) О
0ut [ 82 ] : SVC ( C= 1 , cache size = 200 , class weight = None , coefO=0.0 ,
decision function shape = ' ovr 1 , degree= 3 ,
gamma = ' auto_deprecated ' , kernel = ' linear ' , max iter = - l ,
probability = False , random _ state = None , shrinking = True ,
tol = O . 001 , verbose = False )

_

_

_

_

_

_

In [ 83 ] : pred _ train = model . predict ( train x )

_

_

In [84]: accuracy _score ( train y , pred train )
Out[84]: 0.9402985074626866

©

©

О Обучение модели на тренировочном наборе.

© Прогнозирование меток для тренировочного набора.
© Точность прогнозирования для тренировочного набора ( в пределах
выборки ) .

Далее мы проверяем обученную модель на тестовом наборе. На рис. 13.30
показаны правильные и ложные прогнозы для тестовых данных. Как и следоСтатистический анализ

537

вало ожидать, точность модели на тестовых данных оказалась ниже, чем на
тренировочных данных.
1

]

2

в

1

X

••

X
X

о

X
X

-1

х

X

в
#

-2

-3

-1.0

-1.5

-0.5

0.5

( ). ( )

1 .0

1.5

2.0

Рис. 13.30. Правильные ( точки ) и ложные ( крестики ) прогнозы, полученные
с помощью SVM -модели для тестовых данных

О

.

In [ 85 ] : pred_ test = model predict ( test _ x )

_

In [ 86 ] : test y == pred_ test ©
0ut [ 86 ] : array ( [ True, True, True, True, True, True,
True, True, True, False, False, False,
True, True, False, False, False, True,
True, True, True, True, True, True,
True, True, True, False, True ] )

_

_

In [ 87 ] : accuracy _ score ( test y, pred test )
Out [ 87 ] : 0.7878787878787878

_
_

_

_

True,
True,
True,
True,

©

In [ 88 ] : test c = test x [ test y == pred_ test ]
test f = test _ x [ test _ y ! = pred test ]

.

_

In [ 89 ] : plt figure( figsize= ( 18, 6 ))
plt scatter ( x= test _c [ :, 0], y=test c [ :, 1] , c= test _y [
test _ y == pred_ test ] , marker = ' o ' ,
cmap= ' coolwarm ' )

.

538

Глава 13

_

.

_

_

_

p l t scatter ( x= test f [ :, 0 ] , y= test f [ :, 1], c = test y [
test y ! = pred test ] , marker = ' x ' ,
cmap= ' coolwarm ' ) ;

_

_

О Прогнозирование меток для тестового набора.

© Точность обученной модели для тестового набора (вне выборки).
SVM- классификатор поддерживает несколько вариантов используемых
ядер. При этом, как показывает следующий пример, разные ядра могут приво дить к совершенно разным результатам (т.е. обеспечивать разную точность),
в зависимости от решаемой задачи. В примере сначала выполняется преобразование вещественных признаков в категориальные.

.

I n [ 90 ] : bins = np linspace( - 4.5, 4.5, 50)

.

I n [ 91] : Xd = np digitize ( X, bins =bins )
I n [ 92 ] : Xd [ : 5 ]
0ut [ 92 ] : array ( [ [ 34,
[ 23,
[ 17,
[ 31,
[ 25,

_

21],
21],
18 ] ,
15 ] ,
29 ] ] )

_

_

_

I n [ 93 ] : train x, test x, train y , test _ y = train_ test s p l i t ( Xd , y,
test _ size=0 , 33, random state=8 )
I n [ 94 ] : print ( ' { : >8 s } | { : 8 s } ' . format ( ' Ядро ' ,
print ( 20 * ' - ' )

_

' Точность ' ) )

for kernel in [ ' linear ' , ' poly 1 , ' rbf ' , ' sigmoid ' ] :
model = SVC ( C=1, kernel=kernel, gamma= ' auto ' )
model . f i t ( train_ x, train y )
acc = accuracy score ( test y , model . predict ( test _ x ) )
print ( ' { :>8 s } | { : 8.3 f } ' format ( kernel, acc ) )
Ядро | Точность

_

linear |
poly |
rbf |
sigmoid |

_

.

_

0.848
0.758
0.788
0.455

Статистический анализ

539

Резюме



Статистика
важнейшая дисциплина, которая широко применяется для
решения всевозможных задач в самых разных отраслях, включая финансы
и общественные науки. В одной главе невозможно охватить столь широкую
область знаний. Поэтому мы сфокусировались на четырех ключевых темах,
рассмотрев применение Python и нескольких научных библиотек для решения
прикладных задач.

Нормальное распределение
Предположение о нормальном распределении значений доходности
ценных бумаг лежит в основе многих финансовых теорий и приложений, поэтому важно уметь проверять, справедлива ли эта гипотеза для
конкретного временного ряда. Как было показано, реальные данные о
доходности обычно не соответствуют нормальному распределению.
Оптимизация портфеля

Портфельная теория Марковица, делающая акцент на оценке среднего
значения и дисперсии/ волатильности доходности, стала одним из самых
первых и самых главных достижений в области финансовой статистики.
С ее помощью удобно рассматривать концепцию диверсификации инвестиций.
Байесовская статистика
Байесовская статистика в целом ( и байесовская регрессия в частности)
стала важным инструментом финансовых вычислений, так как позволяет преодолеть недостатки многих подходов, в том числе тех, которые
рассматривались в главе 11. Ее математический аппарат может пока заться достаточно сложным, но основные идеи ( такие, как изменение
степени доверия со временем ) воспринимаются достаточно легко (по
крайней мере, на интуитивном уровне).

Машинное обучение
В наши дни машинное обучение применяется в финансовой среде наря ду с традиционными статистическими методами. В этой главе мы изучили алгоритмы обучения без учителя ( такие, как кластеризация методом
fc -средних) и с учителем ( например, классификаторы глубоких нейронных сетей ), а также рассмотрели ряд дополнительных тем, таких как
преобразование признаков и разделение наборов данных на обучающие
( тренировочные) и тестовые.

540

Глава 13

Дополнительные ресурсы
Дополнительную информацию о рассмотренных в этой главе темах и пакетах можно найти в Интернете:



документация по статистическим функциям SciPy ( https : / / docs .
scipy . org / doc / scipy / reference / stats . html );



документация к библиотеке statsmodels ( http : / / www . statsmodels .
org / stable / );

• детальное описание функций оптимизации ( https : // docs . scipy . огд /
doc / scipy / reference / optimize . html );
• документация к пакету РуМСЗ ( https : / / docs . pymc . io / );
• документация к библиотеке Scikit -learn ( https : / / scikit - learn . org / ) .
В главе упоминались следующие источники.

.

1 Albon, Chris. Machine Learning with Python Cookbook ( 2018, O’ Reilly).

2. Alpaydin, Ethem. Machine Learning ( 2016, MIT Press) .

3. Copeland, Thomas, Fred Weston, and Kuldeep Shastri. Financial Theory and
Corporate Policy ( 2005, Pearson ).
4. Downey, Allen . Think Bayes ( 2013, O’ Reilly).

.

5 Geweke,
Wiley).

John. Contemporary Bayesian Econometrics and Statistics

( 2005,

6. Glasserman , Paul. Monte Carlo Methods in Financial Engineering ( 2004,
Springer ) .

7.



James, Gareth, et al. An Introduction to Statistical Learning With Applications
in R ( 2013, Springer ) .

8. Lopez de Prado, Marcos. Advances in Financial Machine Learning ( 2018,
Wiley).
9. Markowitz, Harry. Portfolio Selection ( 1952,
pp. 77-91).

Journal of Finance, Vol. 7,

10. Rachev, Svetlozar, et al. Bayesian Methods in Finance ( 2008, Wiley).

.

11 VanderPlas, Jake. Python Data Science Handbook ( 2016, O’ Reilly).

Статистический анализ

541

ЧАСТЬ IV

Алгоритмическая торговля

В этой части речь пойдет о применении Python для алгоритмической торговли. Все больше торговых платформ и брокеров позволяют своим клиен там, например, использовать REST-совместимые программные интерфейсы
для получения исторических или потоковых данных, а также для размещения биржевых заявок. То, что долгое время считалось прерогативой крупных
финансовых учреждений, теперь стало доступным даже розничным алгорит мическим трейдерам. В этой среде Python занял доминирующее положение
не только как язык программирования, но и как технологическая платформа.
Среди прочих факторов это обусловлено в первую очередь тем, что многие
торговые площадки, включая FXCM ( Forex Capital Markets), предлагают удобные оболочечные пакеты Python для своих REST-совместимых интерфейсов.
Данная часть включает три главы.

• Глава 14 посвящена знакомству с торговой платформой FXCM, ее программным интерфейсом и оболочечным пакетом fxcmpy.

• Глава 15 посвящена применению методов статистического анализа и
машинного обучения для разработки стратегий алгоритмической торговли. Будет также показано, как выполнять векторизованное тестирование на исторических данных.

• Глава 16 посвящена внедрению автоматизированных стратегий алгоритмической торговли. Речь пойдет об управлении капиталом, тестировании операций на производительность и рисковость, онлайн -алгоритмах и развертывании кода.

ГЛАВА 14

Торговая платформа FXCM
Финансовым учреждениям нравится называть то, что они делают, торговлей.
Давайте будем честными: это не торговля, а ставки.
Грейдон Картер

В этой главе мы познакомимся с торговой платформой FXCM ( Forex Capital
Markets), ее программным интерфейсом, поддерживающим потоковую обра ботку данных, и оболочечным пакетом fxcmpy. FXCM предлагает розничным
и корпоративным трейдерам ряд финансовых продуктов, которые могут торговаться как через традиционные приложения, так и с помощью специализированного программного интерфейса. Основной акцент в этих продуктах делается на валютных парах, а также контрактах на разницу цен, или CFD ( Contract
For Difference), на основные фондовые индексы, биржевые товары и т.п.
Предупреждение о высоких рисках

Я}

Работа на рынках форекс/ CFD связана с высокими рисками и подходит далеко не всем инвесторам, поскольку они рискуют своими залотовыми депозитами. Кредитное плечо может работать против них.
Рассматриваемые продукты предназначены для профессиональных
брокеров. Прежде чем заниматься маржинальной торговлей, убеди тесь, что в полной мере осознаете все риски и последствия прини маемых решений. Старайтесь трезво оценивать свои финансовые
возможности и уровень квалификации. Какая бы информация к вам
ни поступала ( экспертные мнения, новости рынка, данные исследований, аналитические отчеты, ценовые сводки и т.п.), ее нужно лишь
принимать к сведению, но не рассматривать как призыв к инвестиционным действиям. Любые комментарии делаются участниками
рынка в частном порядке, без соблюдения юридических процедур,
гарантирующих независимость инвестиционных решений, и поэтому не подвергаются никакой цензуре. Ни платформа FXCM, ни автор книги не несут ответственности за возможные убытки или фи нансовый ущерб от ( прямого или непрямого) влияния такого рода
информации на принятие инвестором окончательных решений.

Платформа FXCM позволяет внедрять стратегии алгоритмической торговли даже частным трейдерам с небольшим капиталом.
В этой главе рассматриваются основные функциональные возможности
программного интерфейса FXCM и пакета fxcnpy , с помощью которых реали зуются автоматизированные стратегии.
Настройка программного интерфейса FXCM
В этом разделе объясняется, как сконфигурировать среду для работы с
REST-совместимым программным интерфейсом алгоритмической торговли FXCM.

Получение данных
В этом разделе будет показано, как получать и обрабатывать финансовые данные ( в том числе тиковые) .
Работа с программным интерфейсом FXCM
В этом разделе рассматриваются типичные задачи, реализуемые с помощью программного интерфейса FXCM, такие как извлечение исторических и потоковых данных, размещение заявок и поиск учетной информации.

Настройка программного интерфейса FXCM
Подробная документация к программному интерфейсу FXCM доступна по
адресу https : / / f xcn . github . io / rest - apidocs. Для установки оболочечного
пакета f хспру выполните следующую команду в окне консоли:
pip install fxcmpy

Документация к пакету fxcnpy доступна на официальной странице
(http : / / fxcnpy . tpq . io ).
Чтобы начать работу с программным интерфейсом FXCM, достаточно создать бесплатную демонстрационную учетную запись ( https : / / www . fxcn .
con / uk / forex - trading deno / ) 1. Следующий шаг создание для учетной записи уникального API -токена, скажем YOUR _ FXCM_API _ TOKEN . Через этот токен осуществляется подключение к серверу FXCM, например так, как показано ниже.

-

1



Эта возможность доступна не во всех странах. В настоящее время бесплатные учетные записи
могут, в частности, создавать жители Армении, Азербайджана , Грузии, Казахстана , Кирги зии , Молдавии, Таджикистана, Туркмении и Узбекистана. Примеч. ред.



546

Глава 14

import fxcmpy
api = fxcmpy . fxcmpy ( access_ token =YOUR _ FXCM_ API _ TOKEN ,
log_level= ' error ' )

Альтернативный способ заключается в использовании конфигурационно го файла (допустим,fxcm.cfg ) . Содержимое файла должно выглядеть следую щим образом.

[ FXCM ]
log_level = error
log_ file = РАТН_Т0_ AND_NAME_0 F _L0G_FILE
access_ token = YOUR _ FXCM_API _ TOKEN

В таком случае для подключения к серверу FXCM нужно ввести следующий
код.
import fxcmpy
api = fxcmpy . fxcmpy (config_ file= ' fxcm . cfg ' )

По умолчанию класс fxcmpy подключается к демо - серверу. Но с помощью
параметра server можно установить соединение и с действующим торговым
сервером (при наличии соответствующей учетной записи).
api = fxcmpy . fxcmpy ( config_ file= ' fxcm . cfg ' , server = ' demo ' )

О

api = fxcmpy . fxcmpy ( config_ file= ' fxcm . cfg , server = ' real ' )

©

1

О Подключение к демо - серверу.
Q Подключение к действующему торговому серверу.

Получение данных
FXCM предоставляет доступ к историческим наборам данных о рыночной
стоимости акций (содержащим, в частности, тиковые данные) в предварительно упакованном виде. Это означает, что с серверов FXCM можно загрузить сжатые файлы с тиковыми данными, в которых указывается валютный
курс EUR/USD, например, за 26-ю неделю 2018 года. Далее будет также показано, как извлекать исторические свечные данные.

Торговая платформа FXCM

547

Получение тиковых данных
FXCM дает возможность загружать исторические тиковые данные для большого количества валютных пар. Пакет fxcmpy упрощает получение и последующую обработку таких данных. Для начала импортируем все необходимые модули.
In [ 1] : import
import
import
import

time
numpy as np
pandas as pd

datetime as dt
from pylab import mpl, pit

.

.

In [ 2 ] : pit style use ( ' seaborn ' )

.

.

mpl rcParamsf ' font family ' ] = ' serif '
%matplotlib inline

Теперь просмотрим доступные символы (валютные пары), для которых

.

имеются тиковые данные

_

_

In [ 3 ] : from fxcmpy import fxcmpy tick data_ reader as tdr

. __

_

In [ 4 ] : print ( tdr get available symbols ( ) )
( ' AUDCAD ' , ' AUDCHF ' , ' AUDJPY ' , ' AUDNZD ' ,
' EURCHF ' , ' EURGBP ' , ' EURJPY ' , ' EURUSD ' ,
' GBPNZD ' , ' GBPUSD ' , ' GBPCHF ' , ' GBPJPY ' ,
' NZDCHF ' , ' NZDJPY ' , ' NZDUSD ' , ' USDCAD ' ,

' CADCHF ' , ' EURAUD ' ,
' GBPCHF ' , ' GBPJPY ' ,
' GBPNZD ' , ' NZDCAD ' ,
' USDCHF ' , ' USDJPY ' )

Ниже показано, как извлечь недельный массив тиковых данных для отдельной пары. Результирующий объект DataFrame библиотеки pandas содер жит более 1,5 млн строк данных.

.

In [ 5 ] : start = dt datetime ( 2018, б, 25 )
stop = dt datetime( 2018, 6, 30 )

О
О

In [ 6 ] : td = tdr ( ' EURUSD ' , start , stop )

О

.

.

.

In [ 7 ] : td get _ raw _ data ( ) infoQ ©

Index : 1963779 entries, 06 / 24 / 2018 21: 00:12.290 to
06 / 29 / 2018 20 : 59 : 00.607
Data columns ( total 2 columns ) :
Bid
float 64
Ask
float 64
dtypes : float 64 ( 2 )
memory usage : 44.9+ MB

548

Глава 14

.

.

.

_

In [8]: td.get data().infoQ ©

Datetimelndex: 1963779 entries, 2018 06 24 21:00:12.290000 to
2018 06 29 20:59:00.607000
Data columns (total 2 columns):
float64
Bid
float64
Ask
dtypes: float64(2)
memory usage: 44.9 MB

- - -

_

In [9]: td.get data().headQ
0ut[9]:
2018 06- 24 21:00:12.290
2018-06 -24 21:00:16.046
2018-06-24 21:00:22.846
2018-06 - 24 21:00:22.907
2018-06 - 24 21:00:23.441

-

О

Bid

Ask

1.1662
1.1662
1.1662
1.1662
1.1662

1.16660
1.16650
1.16658
1.16660
1.16663

Получение и распаковка файла данных с последующим сохранением не обработанных данных в объекте DataFrame ( в виде атрибута результирующего объекта ) .

_

Q Метод td .get_ raw data() возвращает объект DataFrame, содержащий
необработанные данные ( индексируются строковыми объектами ).
@ Метод td.get data()возвращает объект DataFrame, в котором данные
индексируются с помощью объектов Datetimelndex.

_

Благодаря тому, что тиковые данные хранятся в объекте DataFrame, можно
легко извлекать необходимые поднаборы и выполнять над ними любые анали тические операции. На рис. 14.1 приведены графики среднего спреда и простого
скользящего среднего (Simple Moving Average SMA ) отдельногоподнабора.

In [10]: sub



_

= td .get data(start= ' 2018 - 06 -:19 12:00:'
end = 12018 -06 - 29 12:15:00

In [11]: sub.headQ
0ut[ll]:
2018-06 - 29 12:00:00.011
2018-06- 29 12:00:00.071
2018-06 -29 12:00:00.079
2018-06 29 12:00:00.091
2018-06 - 29 12:00:00.205

-

Bid

Ask

1.16497
1.16497
1.16497
1.16495
1.16496

1.16498
1.16497
1.16498
1.16498
1.16498

In [12]: sub[' Mid '] = sub.mean(axis=l)

©
Торговая платформа FXCM

549

In [13] : sub[ ' SMA ' ]

= sub[ ' Mid ' ] . rolling ( 1080 ) . meanQ ©

In [14 ] : sub[[ ' Mid ' , ' SMA ' ]] . plot ( figsize = ( 18 , 6 ) , lw = 8.75 ) ;

О Выбор поднабора из полного набора данных.

© Вычисление средней разницы между спросом и предложением.
© Вычисление скользящего среднего (SMA) за интервал, равный 1000 тикам.
+ 1.165
МI
SMA

0.0010

0.0008

0.0006

»

0.0004

fc

0.0002

0.0000

Ф

Ф

Ф

.

о

Ф



Ф\

V

Ф\

уov

ТУ

Т>

«

*

Л

'

оД6'

оД Д1

&

Рис. 14.2. Исторические почасовые средние спреды закрытия и два
скользящих средних для валютной пары EUR/ USD
552

Глава 14

'

Работа с программным интерфейсом FXCM
Если в предыдущем разделе речь шла о непосредственной загрузке предварительно упакованных исторических тиковых и свечных данных с сервера
FXCM, то в этом разделе будет показано, как загрузить исторические данные
через программный интерфейс ( API). Для этого нам понадобится объект под ключения. Сначала мы импортируем пакет fxcmpy, затем создадим объект
подключения (инициализируется уникальным API- токеном) и просмотрим
доступные финансовые инструменты.
In [ 27 ] : import fxcmpy

.

version
In [ 28 ] : fxcmpy
Out [ 28 ] : ' 1.1 33 '

.

.

In [ 29 ] : api = fxcmpy fxcmpy( config_ file= '

.. / fxcm. cfg ' )

О

.

In [ 30 ] : instruments = api get _instruments ( )
In [ 31] : print ( instruments )
[ ' EUR / USD ' , ' XAU / USD ' , ' GBP / USD ' , ' UK100 ' , ' USDOLLAR ' ,
' XAG / USD ' , ' GER 30 ' , ' FRA 40 ' , ' USD / CNH ' , ' EUR / JPY ' ,
' USD / JPY ' , ' CHN 50 ' , ' GBP / JPY ' , ' AUD / JPY ' , ' CHF / JPY ' ,
' USD / CHF ' , ' GBP / CHF ' , ' AUD / USD ' , ' EUR / AUD ' , ' EUR / CHF ' ,
' EUR / CAD ' , ' EUR / GBP ' , ' AUD / CAD ' , ' NZD / USD ' , ' USD / CAD ' ,
' CAD / JPY ' , ' GBP / AUD ' , ' NZD / JPY ' , ' US30 ' , ' GBP / CAD ' ,
' SOYF 1 , ' GBP / NZD ' , ' AUD / NZD ' , ' USD / SEK ' , ' EUR / SEK ' ,
' EUR / NOK ' , ' USD / NOK ' , ' USD / MXN ' , ' AUD / CHF ' , ' EUR / NZD ' ,
' USD / ZAR ' , ' USD / HKD ' , ' ZAR / JPY ' , ' BTC / USD ' , ' USD / TRY ' ,
' EUR / TRY ' , ' NZD / CHF ' , ' CAD / CHF ' , ' NZD / CAD ' , ' TRY / JPY ' ,
' AUS 200 ' , ' ESP 35 ' , ' HKG33 ' , ' JPN 225 ' , ' NAS100 ' , ' SPX 500 ' ,
' Copper ' , ' EUSTX 50 ' , ' USOU ' , ' UKOil ' , ' NGAS ' , ' Bund ' ]

О Подключение к программному интерфейсу (укажите правильный путь /
имя файла).

Получение исторических данных
После подключения к программному интерфейсу можно приступать к по лучению данных за необходимые интервалы времени. Это реализуется с помо щью одного -единственного вызова метода get _candels ( ), параметр period
которого может быть равен ml, m5, ml5, m30, HI, Н2, НЗ, Н4, Н6, Н8, Dl, W1или Ml.

Торговая платформа FXCM

553

Ниже приведено несколько примеров . На рис. 14.3 показан график поминут ных цен закрытия предложений для инструмента ( валютной пары ) EUR / USD .
In [32]: candles

=

_

api.get candles(' USD/JPY ' , period ='Dl', number

^

In [33]: candles[candles.columns[:4]] О
bidopen bidclose bidhigh
Out[33]:
bidlow
date
2018-10-08 21:00:00 113.760 113.219 113.937 112.816
2018-10-09 21:00:00 113.219 112.946 113.386 112.863
2018-10-10 21:00:00 112.946 112.267 113.281 112.239
2018- 10-11 21:00:00 112.267 112.155 112.528 111.825
2018- 10-12 21:00:00 112.155 112.200 112.491 111.873
2018- 10-14 21:00:00 112.163 112.130 112.270 112.109
2018- 10-15 21:00:00 112.130 111.758 112.230 111.619
2018- 10-16 21:00:00 112.151 112.238 112.333 111.727

-

2018- 10 17 21:00:00 112.238
2018-10-18 21:00:00 112.636

12.636 112.670 112.009
112.168 112.725 111.942

In [34]: candles[candles.columns[4:]] О
askopen askclose askhigh
Out[34]:
2018-10-08
2018 10 09
2018- 10-10
2018- 10-11
2018-10- 12
2018-10-14
2018-10-15
2018-10-16
2018-10-17
2018-10-18

- -

date
21:00:00
21:00:00
21:00:00
21:00:00
21:00:00
21:00:00
21:00:00
21:00:00
21:00:00
21:00:00

113.840
113.244
112.970
112.287
112.175
112.219
112.181
112.163
112.271
112.664

113.244
112.970
112.287
112.175
112.243
112.181
111.781
112.271
112.664
112.237

113.950
113.399
113.294
112.541
112.504
112.294
112.243
112.345
112.682
112.738

asklow tickqty
112.827
112.875
112.265
111.835
111.885
112.145
111.631
111.740
112.022
111.955

In [35]: start = dt.datetime(2017, 1, 1) ©
end = dt.datetime(2018, 1, 1) ©
In [36]: candles

=

_

api.get candles(' EUR/GBP', period ='Dl' ,
start=start, stop=end)

In [37]: candles.infoQ ©

Datetimelndex: 309 entries, 2017-01 03 22:00:00 to
2018 -01 -01 22:00:00

-

554

Глава 14

©

184835
321755
329174
568231
363233
581
322304
253420
542166
369012

Data columns (total 9 columns):
bidopen
309 non - null float64
bidclose 309 non - null float64
bidhigh
309 non - null float64
bidlow
309 non - null float64
askopen
309 non - null float64
askclose 309 non - null float64
askhigh
309 non - null float64
asklow
309 non - null float64
tickqty
309 non - null int64
dtypes: float64(8), int64(l)
memory usage: 24.1 KB
In [38]: candles

=

_

api.get candles(' EUR/USD ' , period =' ml',
number=258) ©

In [39]: candles[' askclose'].plot(figsize=(10, 6))

О Получение суточных свечей за 10 последних дней.

© Получение суточных свечей за весь год.
© Получение самых последних минутных свечей.

1.1475
1.1470
1.1465
1.1460

1.1455
1.1450

1.1445
1.1440
1.1435

08: 00

09:00

10:00

11: 00

date

.

.

Рис 14.3 Исторические цены закрытия предложений для валютной
пары EURJUSD ( минутные свечи )
Торговая платформа FXCM

555

Получение потоковых данных
Исторические данные важны для тестирования стратегий алгоритмической торговли , но для развертывания автоматизированных торговых систем
необходим непрерывный доступ к данным реального времени ( потоковым
данным ) . Программный интерфейс FXCM позволяет подписаться на потоко вые данные для всех финансовых инструментов. Оболочечный пакет f xcmpy
поддерживает работу с потоковыми данными через так называемые функции
обратного вызова, предоставляемые пользователем .
В следующем примере создается простая функция обратного вызова, кото рая отображает выбранные элементы извлекаемого набора данных. Эта функ ция применятся к потоковым данным финансового инструмента, на который
оформлена подписка ( в нашем случае EUR / USD).
In [ 40 ] : def output ( data , dataframe ) :
print( '%3 d | %s | %s | %6.5 f, %6.5f ' %
(len(dataframe), data['Symbol'],
pd .to_ datetime(int(data[' Updated ']), unit=' ms'),
data[' Rates'][ )]* data[ ' Rates'][!])) О
In [ 41] : api.subscribe_ market _ data(' EUR /USD', (output,)) ©
1 1 EUR /USD 1 2018 - 10 - 19 11:36:39.735000 i 1.14694,
2 1 EUR / USD 1 2018 - 10 - 19 11:36:39.776000 i 1.14694,
3 1 EUR / USD 1 2018 - 10 - 19 11:36:40.714000 i 1.14695,
4 1 EUR /USD 1 2018 - 10 - 19 11:36:41.646000 i 1.14696,
5 1 EUR / USD 1 2018 - 10 - 19 11:36:41.992000 i 1.14696,
6 1 EUR /USD 1 2018 - 10 - 19 11:36:45.131000 i 1.14696,
7 1 EUR /USD 1 2018 - 10 - 19 11:36:45.247000 i 1.14696,

1.14705
1.14706
1.14707
1.14708
1.14709
1.14708
1.14709

In [ 42 ] : api.get _last _ price(' EUR / USD') ©
Out[ 42 ]: Bid 1.14696
Ask 1.14709
High 1.14775
Low 1.14323
Name: 2018 - 10 - 19 11:36:45.247000, dtype: float64
In [ 43] : api.unsubscribe _ market _ data( ' EUR / USD') 0
8 | EUR /USD | 2018 - 10 - 19 11:36:48.239000 | 1.14696, 1.14708

О Функция обратного вызова, которая выводит отдельные элементы извлекаемого набора данных.

556

Глава 14

© Подписка на конкретный поток данных реального времени. Данные об рабатываются в асинхронном режиме, пока пользователь не отпишется
от них.

©

_

_

Пока действует подписка, метод get last price ( ) возвращает послед ний доступный набор данных.

0 Отмена подписки на потоковые данные.

(

Функции обратного вызова
Функции обратного вызова — гибкое средство обработки потоко вых данных, поступающих в режиме реального времени. Их можно
к использовать как для выполнения простых операций (например,
вывод поступающих данных), так и для решения более сложных
задач, включая генерацию торговых сигналов, основанных на алго ритмах интернет - трейдинга (глава 16).

Размещение заявок
Программный интерфейс FXCM позволяет управлять любыми типами заявок, доступными также через торговое приложение FXCM (включая лимит ные заявки и стоп - лоссы) 2. Однако в следующем примере демонстрируется
лишь, как создавать простые рыночные заявки, так как этого достаточно для
знакомства с принципами алгоритмической торговли. Сначала мы убеждаемся в отсутствии открытых позиций, а затем открываем несколько разных по зиций с помощью метода create_market _buy _order ( )

.

О

.

In [ 44 ] : api get _open_positions ( )
0ut [ 44 ] : Empty DataFrame

Columns : [ ]
Index : [ ]

_

.

In [ 45 ] : order = apt create_market buy _ order ( ' EUR / USD ' , 10 )

In [ 46 ] : sel = [ ' tradeld ' ,
' grossPL ' ,

. _

' amountK ' , ' currency ' ,
' isBuy ' ] ©

_

In [ 47 ] : api get open positions ( ) [ sel ] ©
0ut [ 47 ] :
grossPL
tradeld amountK currency
0 132607899
10
0.17436
EUR / USD
2

©

isBuy
True

.

Детали можно узнать в документации (http: / / fxcmpy tpq .io / ).

Торговая платформа FXCM

557

_

.

In [ 48 ] : order = api create_market buy _order ( ' EUR / GBP ' , 5 )

О

.

In [ 49 ] : api get _open _positions ( ) [ sel ]
0ut [ 49 ] :
tradeld anountK currency
0 132607899
10
EUR / USD
1 132607928
EUR / GBP
5

grossPL
0.17436

- 1.53367

isBuy
True
True

О Отображение сведений об открытых позициях текущей учетной записи.

© Открытие позиции размером 10 000 для валютной пары EUR / USD3.
© Отображение открытых позиций только для выбранных элементов.

0 Открытие еще одной позиции размером 5 000 для валютной пары EUR / GBP.

_
_

_

_

Функция create market buy order ( ) создает заявку на покупку, а функ ция create market sell order ( ) — заявку на продажу. Существуют также
более общие методы, позволяющие закрывать позиции, как показано ниже.

_

_

_

.

In [ 50 ] : order = api create_market _sell order ( ' EUR / USD ' , 3 )

_

.

In [ 51] : order = apt create_narket _buy order ( 1 EUR / GBP ' , 5 )

О

©

.

In [ 52 ] : api get _ open_positions ( ) [ sel ] ©
tradeld anountK currency grossPL
Out [ 52 ] :
0.17436
10
0 132607899
EUR / USD
1 132607928
5
EUR / GBP - 1.53367
3
2 132607930
EUR / USD - 1.33369
3 132607932
5
EUR / GBP - 1.64728

. _ _ _
api. get _open_positions ( ) [ sel ]

In [ 53 ] : api close all for synbol ( ' EUR / GBP ' )
In [ 54 ] :
0ut [ 54 ] :

tradeld anountK
132607899
1 132607930

0

_

.

In [ 55 ] : api close all( )

3

10
3

currency
EUR / USD
EUR / USD

isBuy
True
True

False
True

©

grossPL

0.17436
- 1.33369

isBuy
True

False

©

Размер позиции указывается в тысячах единиц. Также учтите, что для разных учетных запи сей могут устанавливаться разные маржинальные требования (https : / / www fxcn con / uk /
accounts / forex - cfd leverage / ) . Это означает, что для одной и той же позиции может потребоваться больший или меньший залоговый депозит, в зависимости от плеча финансового
рычага . В случае необходимости уменьшите соответствующие значения при создании заявок.

-

558

Глава 14

.

.

.

In [ 56 ] : api get _ open_positions ( )
Out [ 56 ] : Empty DataFrame

Columns : [ ]
Index : [ ]

О Понижение позиции в валютной паре EUR / USD.
© Повышение позиции в валютной паре EUR / GBP.
© Теперь инструмент EUR / GBP представлен двумя открытыми позициями

покупки, тогда как для инструмента EUR / USD имеется позиция покупки
и позиция продажи.

© Метод close_all_ for _ symbol( ) закрывает все позиции для указанного
символа.

© Метод close_all( ) закрывает все открытые позиции.

Учетные данные
Помимо информации об открытых позициях программный интерфейс
FXCM позволяет получать общие сведения об учетной записи. Можно, например, просмотреть идентификатор учетной записи, заданной по умолчанию
(если есть несколько учетных записей), а также узнать величину капитала и
маржи.

.

In [ 57 ] : api get _default _ account ( )
Out [ 57 ] : 1090495

.

.

In [ 58 ] : apl get _accounts ( ) T
0ut [ 58 ] :

accountld
accountName

balance
dayPL
equity
grossPL

hedging
me
mcDate
ratePrecision
t

usableMargin
usableMargin3
usableMargin3Perc

©

©

0
1090495
01090495
4915.2
- 41.97
4915.2
0
Y
N

0
6
4915.2
4915.2
100

Торговая платформа FXCM

559

usableMarginPerc

100

usdMr

0
3

usdMr

О Вывод идентификатора учетной записи, заданной по умолчанию.

© Отображение финансовой ситуации и отдельных

показателей для всех

учетных записей.

Резюме
В этой главе, посвященной REST- совместимому программному интерфейсу алгоритмической торговли FXCM, рассматривались следующие темы:



конфигурирование программного интерфейса;







получение исторических тиковых данных;
получение исторических свечных данных;
получение потоковых данных в режиме реального времени;

размещение рыночных заявок на покупку и продажу;
просмотр информации об учетной записи.

Разумеется, функциональные возможности программного интерфейса
FXCM и оболочечного пакета fxcmpy значительно шире, но приведенных в
этой главе сведений вполне достаточно для того, чтобы начать заниматься алгоритмической торговлей.

Дополнительные ресурсы
Дополнительные сведения о программном интерфейсе FXCM и оболочечном пакете fxcmpy содержатся в документации:

.

.

• программный интерфейс FXCM (https : / / fxcm github io / rest - api docs / );
• пакет fxcmpy (https : / / fxcmpy . tpq.io / ).
Специализированный онлайн-курс, посвященный использованию Python
для алгоритмической торговли, доступен на сайте http : / / certificate ,
tpq io

. .

560

Глава 14

ГЛАВА 15

Торговые стратегии
Они были достаточно глупы, чтобы поверить, будто вы сможете предсказать
будущее, заглянув в прошлое.
The Economist

Эта глава посвящена векторизованному тестированию алгоритмических
торговых стратегий на исторических данных. Термин алгоритмическая тор говая стратегия используется для описания любого типа торговой стратегии, которая основана на алгоритме, способном самостоятельно создавать
открытые длинные, открытые короткие и закрытые позиции по финансовым
инструментам без участия трейдера. Данному определению соответствует,
например, такой простой алгоритм, как “ каждые пять минут переходить от
длинной к закрытой позиции по акциям компании Apple” Для целей главы мы
будем представлять алгоритмическую торговую стратегию в виде кода Python,
который при наличии новых данных принимает решение о покупке или продаже финансового инструмента, создавая длинные, короткие или закрытые
позиции.
В главе не дается обзор всех доступных стратегий алгоритмической торговли ( об этом можно прочитать в специализированной литературе, упомянутой
в разделе “ Дополнительные ресурсы ” ). Вместо этого мы сконцентрируемся
на технических аспектах векторизованного тестирования нескольких таких
стратегий на исторических данных. В векторизованном подходе финансовые данные, на которых проверяется стратегия, рассматриваются как единое
целое, и соответствующие операции выполняются сразу над всем объектом
ndarray библиотеки NumPy или объектом DataFrame библиотеки pandas 1.
Еще одна тема главы
применение алгоритмов машинного и глубокого
обучения для реализации алгоритмических торговых стратегий. Мы будем
обучать алгоритмы классификации на исторических данных, чтобы иметь



1



Альтернативный подход событийное тестирование торговых стратегий, в рамках которого появление новых рыночных данных моделируется путем явного прохода по каждой новой
точке данных.

возможность предсказывать будущие колебания рынка. Обычно для этого
требуется преобразовать финансовые данные из вещественных значений в от носительно небольшое количество категориальных значений2, что позволяет
задействовать методы распознавания образов.
В главе рассматриваются следующие темы.

Простое скользящее среднее
В этом разделе рассматриваются алгоритмические торговые стратегии,
основанные на простом скользящем среднем, и способы их тестирования на исторических данных.
Гипотеза случайного блуждания
В этом разделе мы рассмотрим гипотезу случайного блуждания.
Линейная регрессия по методу наименьших квадратов
Этот раздел посвящен применению регрессии по методу наименьших
квадратов для разработки алгоритмической торговой стратегии.

Кластеризация
В этом разделе мы рассмотрим применение технологий машинного обучения без учителя для разработки алгоритмической торговой стратегии.
Частотный подход
В этом разделе рассматривается простой частотный подход к алгоритмической торговле.

Классификация
В этом разделе мы рассмотрим применение алгоритмов классификации
в алгоритмической торговле.
Глубокие нейронные сети
В этом разделе мы поговорим о применении глубоких нейронных сетей
в алгоритмической торговле.

Простое скользящее среднее
Торговые стратегии на основе простого скользящего среднего (Simple
SMA ) распространены уже достаточно давно ( см., напри Moving Average
мер, работу Брока и соавторов [ 2] ). Несмотря на то что трейдеры применяют



2

При работе с вещественными значениями каждый шаблон может оказаться уникальным или,
по крайней мере, довольно редким, что затрудняет обучение алгоритма и не позволяет делать
никаких выводов из факта распознавания такого образа.

562

Глава 15

SMA преимущественно в ручной торговле, этот метод может также стать
основой для формирования простой алгоритмической стратегии. В данном
разделе мы будем использовать SMA для векторизованного тестирования алгоритмических стратегий на исторических данных. Мы продолжим пример
технического анализа, начатый в главе 8.

Импорт данных
Начнем с импорта модулей.
In [1] : import numpy as np
import pandas as pd
import datetime as dt
from pylab import mpl, pit

.
.

.

In [ 2 ] : pit style use ( ' seaborn ' )
mpl rcParams [ ' font family ' ] =
%matplotlib inline

.

' serif '

Далее необходимо загрузить необработанные данные и выбрать финансовый
временной ряд для единственного тикера, представляющего акции компании
Apple, Inc. (AAPL . 0). В этом разделе мы будем анализировать данные на конец
торгов. Внутридневные данные будут обрабатываться в последующих разделах.

.

.. / .. / source / tr _eikon_eod_data .csv ' ,
index _col=0, parse_dates=True )

_

In [ 3 ] : raw = pd read csv ( '

In [ 4 ] : raw

.infoQ

.

.

' pandas core. frame DataFrame ' >
Datetimelndex : 2216 entries, 2010 - 01- 01 to 2018 - 06 - 29
Data columns ( total 12 columns ) :
2138 non - null float 64
AAPL.0
2138 non - null float 64
MSFT O
2138 non - null float 64
INTC O
2138 non - null float 64
AMZN O
2138 non - null float 64
GS . N
2138 non - null float 64
SPY
2138 non - null float 64
. SPX
. VIX
2138 non - null float 64
EUR=
2216 non - null float 64
XAU=
2211 non - null float 64
GDX
2138 non - null float 64
2138 non - null float 64
GLD


пРхг

пР&

пР
г*
*
Date
43 "

пРхЛ

пРхЪ

Рис . 15.1 . Цена акций компании Apple и два простых скользящих средних
564

Глава 15

Наконец, приступим к формированию торговой стратегии. Правила трей динга будут такими:



если линия краткосрочного SMA оказывается выше линии долгосроч ного SMA, открываем длинную позицию ( = + 1 );

• если линия краткосрочного SMA оказывается ниже линии долгосрочного SMA, открываем короткую позицию ( = -1).
Соответствующие графики показаны на рис. 15.2.
200

... All/

180

AAPL.O
SMA1
SMA2
Позиция

160
140

. >*4

120

УУ
УУ
22

л

100

.

УУ w

ao

1.00

A

0.75

FZ
JZ
'%y

0.50
0.25
0.00

-0.25

" YT

-0.50

^

-0.75

40

-1.00

^

7$

Date

Рис. 15.2. Цена акций компании Apple, два скользящих средних и

получаемые позиции

In [ 10 ] : data.dropna(inplace=True)

In [ 11 ]: data['Позиция'] = np.where(data[' SMA 1 ' ]
i

.-

i)

о

>

data[' SMA 2 ' ],

In [ 12 ] : data.tailQ
0ut[12]:
Date
2018 - 06 - 25
2018 - 06 - 26
2018 06 - 27
2018 - 06 - 28
2018 06 - 29

-

AAPL . O

SMA1

SMA2

Позиция

182.17
184.43
184.16
185.50
185.11

185.606190
186.087381
186.607381
187.089286
187.470476

168.265556
168.418770
168.579206
168.736627
168.901032

1
1
1
1
1

Торговые стратегии

565

_
_ _

d a t a . p l o t ( s e c o n d a r y y = ' П о з и ц и я ' , f i g s i z e= ( 1 8 , 6 ) ,
rnark r i g h t = F a l s e )
ax . g e t _ l e g e n d ( ) . s e t _ bbox t o a n c h o r ( ( G . 2 5 , 0 . 8 5 ) ) ;

I n [ 1 3]: a x

_

=

О Функция np . where ( cond , o , b ) поэлементно проверяет условие cond,
возвращая а, если условие истинно ( True ), и b



в противном случае.

Как видите, результаты совпадают с теми, которые мы получили в главе 8.
Единственное, что мы не рассматривали, это вопрос о том, дает ли соблюдение алгоритмической торговой стратегии какие-то преимущества по сравнению с эталонным случаем, когда на протяжении всего периода мы удерживаем
длинную позицию по акциям Apple. Поскольку в рамках стратегии мы лишь
дважды переходим к коротким позициям, только на этих двух отрезках могут
проявиться различия в доходности.



Векторизованное тестирование на исторических данных
Теперь можно перейти к векторизованному тестированию торговой стратегии на исторических данных. Сначала необходимо вычислить логарифми ческие доходности, которые затем умножаются на весовые значения позиций
( + 1 или -1). Такой простой подход возможен, поскольку длинная позиция по
ценным бумагам Apple обеспечивает положительную, а короткая позиция
отрицательную доходность. Для определения общей доходности остается
только суммировать значения логарифмической доходности акций Apple и
алгоритмической торговой стратегии, основанной на SMA, и применить к по-



лученному результату экспоненциальную функцию.
I n [1 4 ] : d a t a [ ' Д о х о д н о с т ь ' ]

I n [1 5 ] : d a t a [ ' С т р а т е г и я ' ]

.

=
=

.

n p l o g (d a t a[ s y m b o l] /
data[ symbol] s h i f t( l ) )

.

О

d a t a[ ' Позиция ' ].s h l f t ( l ) *
data[ ' Доходность ' ] ©

.

I n [1 6 ] : d a t a r o u n d ( 4 ) h e a d ( )
O u t [1 6] :

Date
2010 12 31
2011-01-03
2011-01-04
2011 01-05

- -

-

2011-01-06

566

Глава 15

AAPL.O

SMA1

46.0800
47.0814
47.3271
47.7142
47.6757

45.2810
45.3497
45.4126
45.4661
45.5226

SMA 2 Позиция
37.1207
37.1862
37.2525
37.3223
37.3921

1
1
1
1
1

Доходность Стратегия
Date

2010
2011
2011
2011
2011

-12 - 31

-0101-0403
-01-- 05
-01 06
-

-

-

NaN

NaN

0.0215
0.0052
0.0081
0.0008

0.0215
0.0052
0.0081
0.0008

-

In [17]: data.dropna(inplace=True)
In [18]: np.exp(data[['Доходность' , ' Стратегия']].sum())
0ut[18]: Доходность 4.017148
Стратегия
5.811299
dtype: float64

©

In [19]: data[[' Доходность' , ' Стратегия ']].std() * 252 ** 0.5 ©
Out[19]: Доходность 0.250571
Стратегия
0.250407
dtype: float64

О Вычисление логарифмической доходности акций Apple ( эталонная инвестиция).

© Умножение весового коэффициента позиции, заданного с суточным смещением, на значение логарифмической доходности; временной сдвиг позволяет избежать смещенного прогноза3.

© Суммирование логарифмических доходностей эталонной инвестиции и

алгоритмической торговой стратегии с последующим применением экспоненциальной функции для вычисления абсолютной доходности.

© Вычисление годовой волатильности для эталонной инвестиции и алго ритмической торговой стратегии.

Цифры говорят о том, что выбранная алгоритмическая торговая стратегия
оказывается эффективнее эталонной инвестиции, предполагающей пассивное удержание акций Apple. Тип и характер торговой стратегии обеспечивают
такую же годовую волатильность, как и у эталонной инвестиции, благодаря
чему стратегия показывает лучшие результаты с поправкой на риск.

3

Идея состоит в том, что алгоритм может открыть позицию по акциям Apple только на основании сегодняшних рыночных данных (до завершения торгов ). Соответственно, доходность
позиции рассчитывается для следующего дня.

Торговые стратегии

567

Чтобы нагляднее увидеть разницу в доходности, рассмотрим рис. 15.3, на
котором сравниваются графики доходности акций компании Apple и алгоритмической торговой стратегии.
In [ 20 ] : ах = data [[ ' Доходность ' , ' Стратегия ' ]] . cumsum (
) . apply ( np . exp ) . plot ( figsize = ( 18 , 6 ) )
data[ ' Позиция ' ] . plot ( ax =ax , secondary _ y = ' Позиция ' ,
style = ' - - ' )
ax . get _ legend ( ) . set _ bbox _ to_anchor ( ( 0.25 , 0.85 ) ) ;
1.00

б

0.75

Доходность

Стратегия

5

.

4

3

2

,

2

568



,
.

I,

Л

и/

'

0.50

.

. г\лГ
Г
/ *'

0.25
0.00

-0.25
-0.50

I
I
I

PWf "

***

I

-0.75

I

I

I

-1.00

^ ^ ^

Date

.

Puc. 15.3 Изменение доходности акций Apple и торговой
стратегии на основе SMA

Упрощения
Векторизованное тестирование торговой стратегии на исторических данных предполагает ряд упрощений. Прежде всего, здесь
не учитываются трансакционные издержки ( фиксированные комиссии, кредитные расходы и т.п.). Это приемлемо только в торговых стратегиях, предполагающих совершение всего нескольких
сделок за несколько лет. Также предполагается, что все сделки совершаются по ценам закрытия, зафиксированным на конец торгов.
В более реалистичном подходе все эти и многие другие микро структурные рыночные факторы нужно обязательно учитывать.

Глава 15

Оптимизация
Возникает логичный вопрос о том, насколько оправдан выбор параметров
SMA1=42 и SMA 2=252. Понятно, что при прочих равных условиях инвесторы
предпочитают более высокую доходность более низкой. Поэтому можно по стараться подобрать такие параметры, которые обеспечат максимальную до ходность за требуемый период времени. Одно из решений — метод грубой
силы, при котором вся процедура векторизованного тестирования повторяет ся для всех возможных комбинаций параметров, после чего выбирается луч ший из результатов. Ниже реализован именно такой подход.
In [ 21] : from itertools import product
In [ 22 ] : smal = range ( 20, 61, 4) О
sma 2 = range( 180, 281, 10 )

©

.

In [ 23 ] : results = pd DataFrameQ
for SMA1, SMA 2 in product ( smal, sma 2 ) : ©
data = pd DataFrame ( raw [ symbol ] )
data dropna ( inplace=T rue )
data [ ' Доходность ' ] = np log( data [ symbol ] /
data [ symbol ] shift (l) )
data [ ' SMAl ' ] = data [ symbol ] rolling( SMAl) mean ( )
data [ ' SMA 2 ' ] = data [ symbol ] rolling( SMA 2 ) meanQ
data dropna ( inplace=True )
data [ ' Позиция ' ] = np where ( data [ ' SMA1' ] >
data [ ' SMA 2 ' ] , 1, - 1)
data [ ' Стратегия ' ] = data [ ' Позиция ' ] shift (l) *
data [ ' Доходность ' ]
data dropna ( inplace=True )
perf = np exp( data [ [ ' Доходность ' , ' Стратегия ' ] ] sum ( ) )
results = results append ( pd DataFrame (
{ ' SMA1' : SMA1, ' SMA 2 ' : SMA 2,
' MARKET ' : perf [ ' Доходность ' ] ,
' STRATEGY ' : perf [ ' Стратегия ' ] ,
' OUT ' : perf [ ' Стратегия ' ] - perf [ ' Доходность ' ] },
index= [ Q ] ) , ignore index=True ) ©

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

_

О
©
©
©

Диапазон значений для параметра SMA1.
Диапазон значений для параметра SMA 2.

Перебор всех комбинаций параметров SMA1и SMA 2.
Сохранение результатов векторизованного тестирования в объекте
DataFrame.
Торговые стратегии

569

В следующем коде выводятся семь наиболее эффективных комбинаций
параметров. Ранжирование проводится по разнице между доходностью алго ритмической торговой стратегии и эталонной инвестиции. Учтите, что доход ность эталонной инвестиции тоже варьируется, поскольку значение параметра SMA 2 влияет на длительность временного интервала и объем данных, для
которых выполняется векторизованное тестирование.

In [ 24 ] : results.infoQ
< class ' pandas.core.frame. DataFrame ' >
Rangelndex: 121 entries, 0 to 120
Data columns (total 5 columns):
SMA 1
121 non - null Int64
SMA 2
121 non - null int64
121 non - null float64
MARKET
121 non - null float64
STRATEGY
121 non - null float64
OUT
dtypes: float64(3), int64( 2)
memory usage: 4.8 KB
In [ 25 ] : results.sort _ values('OUT', ascending = False).head(7)
Out [ 25 ] :
SMA1 SMA 2

MARKET

STRATEGY

OUT

190
240
220
200
180
220
200

4.650342
4.045619
4.220272
4.074753
4.574979
4.220272
4.074753

7.175173
6.558690
6.544266
6.389627
6.857989
6.469843
6.319524

2.524831
2.513071
2.323994
2.314874
2.283010
2.249571
2.244772

56
39
59
46
55
70
101

40
32
40
36
40
44
56

Согласно полученным результатам оптимальными будут параметры
SMA1 = 40 и SMA 2 = 190, обеспечивающие 250-процентную разницу в доходности. Однако оптимизация сильно зависит от используемого набора данных,
что может привести к переобучению модели. Более строгий подход предпо лагает выполнять оптимизацию параметров на одном наборе данных (обучающем, в пределах выборки), а векторизованное тестирование — на другом
наборе (тестовом, вне выборки).

Переобучение
Как правило, любой метод оптимизации, подгонки или обучения
модели в контексте построения алгоритмических торговых стратегий подвержен проблеме переобучения. Это означает, что существует риск выбрать такие параметры, которые будут давать

570

Глава 15

( исключительно) хорошие результаты для исходного набора дан ных и ( исключительно) плохие результаты для других наборов дан ных, встречающихся на практике.

Гипотеза случайного блуждания
В предыдущем разделе вы узнали, как выполнить векторизованное тестирование алгоритмических торговых стратегий на исторических данных ( рет роспективное тестирование ) . Тестирование отдельной стратегии на един ственном временном ряде, представляющем исторические данные о ценах
акций Apple на конец торгов, показало, что ее доходность выше доходности
эталонной инвестиции, предполагающей удерживание длинных позиций по
акциям Apple в течение аналогичного периода времени.
Но это, скорее, частный случай. Результаты тестирования противоречат
RWH ), котогипотезе случайного блуждания ( Random Walk Hypothesis
рая утверждает, что такого рода прогнозы не должны предсказывать ника кого прироста доходности. Согласно гипотезе цены на финансовых рынках
подвержены случайному блужданию и в непрерывном времени описываются
моделью арифметического броуновского движения без смещения ( дрейфа ).
Ожидаемое значение такого броуновского движения в любой будущей временной точке равно его значению сегодня4. Как следствие, в гипотезе случай ного блуждания наилучшим предсказанием будущей цены, рассчитываемой
по методу наименьших квадратов, будет сегодняшняя цена.
Такой вывод подкрепляется следующей цитатой.



В течение многих лет экономисты , математики и специалисты по финансам за нимались разработкой и тестированием моделей поведения биржевых котиро вок. Результатом их исследований стало появление теории случайных блужда ний. Эта теория подвергает сомнению многие другие методы , применяемые
для описания и предсказания поведения биржевых котировок и получившие
широкое распространение вне академической среды. Как будет показано далее,
если теория случайных блужданий достаточно точно описывает действитель ные биржевые процессы, то различные “ околонаучные” методики предсказа ния котировок акций оказываются совершенно бесполезными.
Юджин Фама [ 3 ]

Гипотеза случайного блуждания прекрасно согласуется с гипотезой эффективного рынка ( Efficient Markets Hypothesis ЕМН ) , которая в целом утвержда -



4

Формальное определение и подробное описание процессов случайного блуждания и броуновского движения приведены в книге Бакстера и Ренни [ 1 ].

Торговые стратегии

571

ет, что рыночные котировки отражают “ всю доступную информацию ”. Различа ют три формы эффективности рынка слабую, среднюю и сильную, каждая
из которых очерчивает свои рамки “ всей доступной информации” С формальной точки зрения такое определение основывается на концепции информаци онного множества в теории игр, что подтверждается следующей цитатой.





Рынок эффективен в отношении информационного множества S, если невоз можно получить прибыль, занимаясь торговлей на основе этого множества .
Майкл Дженсен [4 ]

В Python гипотезу случайного блуждания можно проверить для конкретного случая. Берется финансовый временной ряд, содержащий исторические
данные о рыночных ценах, и для него создается несколько смещенных версий,
допустим, пять. Далее к ним применяется регрессия по методу наименьших
квадратов для прогнозирования рыночных цен. Основная идея заключается
в том, что на основе вчерашней цены и цен за предыдущие четыре дня можно

предсказать сегодняшнюю цену.
Ниже показано, как реализовать такой алгоритм на Python. В этом примере
создается пять смещенных версий временного ряда, содержащего исторические данные об уровнях индекса S&P 500 на конец торгов.
In [ 26 ] : symbol

=

'.SPX'

In [ 27 ] : data

=

pd.DataFrame(raw[symbol])

In [ 28 ] : lags

=

5

cols = []
for lag in range ( l , lags + 1):
col = ' lag {}'.format(lag)©
data[col] = data[symbol].shift(lag)©
cols.append(col)©

_

In [29]: data.head(7)
Out[ 29 ] :

.SPX

lag _l

lag _ 2

lag _3

lag _4

Date
2010 01 01

NaN

2010-01-04
2010-01-05
2010-01-06
2010 01-07
2010 01-08
2010-01-11

1132.99
1136.52
1137.14
1141.69
1144.98
1146.98

NaN
NaN

NaN
NaN
NaN

NaN
NaN
NaN
NaN

NaN
NaN
NaN
NaN
NaN

- -

-

572

Глава 15

1132.99
1136.52
1137.14
1141.69
1144.98

1132.99
1136.52 1132.99
1137.14 1136.52 1132.99
1141.69 1137.14 1136.52

_

1ад 5
Date
2010 - 01- 01
2010 - 01- 04
2010 - 01- 05
2010 - 01- 06
2010 - 01- 07
2010 - 01- 08
2010 - 01 - 11

NaN
NaN
NaN
NaN
NaN
NaN
1132.99

In [ 30 ] : data .dropna ( inplace = True )

О Определение названия столбца для текущей копии набора данных.
@

Создание смещенной версии набора данных о ценах.

@

Создание массива имен столбцов для дальнейшего использования.

Регрессию по методу наименьших квадратов проще всего выполнить средствами библиотеки NumPy. Как показывает анализ регрессионных параметров, наибольшее влияние на точность предсказания рыночных цен оказывает ряд 1ад 1.
Его вклад близок к 1. Вклады остальных четырех рядов близки к 0 ( рис. 15.4).

_

1.0

0.8

0.6

0.4

0.2

0.0

_

1ад 1

.

lag 2

lag 3

_

1ад 4

_

1ад 5

.

Рис 15.4 Оптимальные регрессионные параметры при прогнозировании
цен по методу наименьших квадратов

Торговые стратегии

573

При сравнении прогнозируемых значений, соответствующих оптимальным параметрам регрессии, с действительными значениями индекса S&P 500
становится очевидным, что прогноз строится преимущественно на основе
ряда lag l. На рис. 15.5 график предсказаний повторяет график исходного
временного ряда, смещенного на один день вправо ( с незначительными корректировками).

_

2775

2750
2725
2700
2675

2650

2625
2600

.SPX
Прогноз

.

Л'Ь

#°v

>

«

оЯЬ

*

Я

'

&

in Направление

Date
2010-01-07
2010-01-08
2010 01-11
2010 01-12
2010 01 13

-

1
0
1
1
0

-

-11

0
1
0
1
1

1

-1
1

_

In [48]: grouped = data.groupby(cols bin + ['Направление'])
grouped.sizeQ ©
Out[48]: lag l bin lag 2 bin Направление

__

__

0

0

1

0

1

1

-1
-

-

239
4
258
262
288
272
1
278
278
4
251

0
1
1
1
1
0
1
1
0
1

dtype: int64
In [49]: res

=

_

grouped['Направление '].sizeQ.unstack(fill value=0)

_

In [50]: def highlightjnax(s):
is max = s == s.maxQ
return [' background color: yellow' if v else ''
for v in is max] ©

_-

_

In [51]: res.style.apply(highlight nax, axis=l)
0ut[51]:
lag _l_ bin

-

Направление
1 0
1
lag _ 2_ bin
0 239 4 258

0
1 262 0 288
0 272 1 278
1

278 4 251

584

Глава 15

©

©

О Дискретизация значений признаков с учетом параметра bins.

© Отображение дискретных значений признаков и меток.
© Отображение частоты возможных движений в зависимости от комбинаций значений признаков.

© Преобразование объекта DataFrame, чтобы частоты хранились по столбцам.

0 Определение наиболее часто встречающегося значения для каждой ком бинации значений признаков.

Три комбинации значений признаков указывают на большую вероятность
движения вниз, и только для одной из них более вероятным оказывается дви жение вверх. На основе этого можно построить торговую стратегию, график
доходности которой показан на рис. 15.11.
1.10
1.05
1.00
0.95
0.90
0.85
0.80
0.75

Доходность
stratfreq

Р

Л'

п>

^

пР&
Date

пР&

ъ

то*

Рис. 15.11. Доходность валютной пары EURJUSD и торговой стратегии,
основанной на частотном подходе
In [ 52 ] : data[ ' pos _ freq ' ]

=

_

np . where ( data [ cols bin ] . sum ( axis = l )
2 , - 1, 1 ) ©

In [ 53 ] : ( data [ ' Направление ' ]
Out [ 53 ] : True
1102
False
1033
dtype : int 64

==

== data[ ' pos _freq ' ] ) . value_counts ( )

Торговые стратегии

585

In [54]: data [ ' strat _ freq ' ] = data [ pos _ freq ' ] * data [ ' Доходность ' ]
1

In [55]: data [ [ ' Доходность ' , ' strat _ freq ' ] ] . sun( ) . apply ( np . exp )
Out [ 55 ] : Доходность
0.810644
strat_ freq
0.989513
dtype : float 64

: data [ [ ' Доходность ' , ' strat _ freq ' ] ] . cumsum(
In [56]
) . apply ( np . exp ) . plot ( figsize= ( 10 , 6 ) ) ;

О Преобразование полученного

частотного распределения в торговую

стратегию.

Классификация
В этом разделе мы воспользуемся алгоритмами машинного обучения (см.
главу 13) для решения задачи о прогнозировании направления движения цен
на финансовых рынках. С учетом примеров из предыдущих разделов применить логистическую регрессию, наивный байесовский классификатор и метод
опорных векторов будет не намного сложнее, чем в случае обработки неболь шого набора выборочных данных.

Два бинарных признака
Для начала напишем код обучения моделей на основе двух бинарных признаков и получения соответствующих значений позиций.
In [57]: from
from

sklearn

import linear _model

__

sklearn.naive bayes import GaussianNB
from sklearn.svm import SVC

In [58]: С

=1

In [59]: models = {
' log_ reg ' : linear _model . LogisticRegression ( C=C ) ,
' gauss _nb ' : GaussianNB ( ) ,
' svm ' : SVC ( C=C )
}

586

Глава 15

In [ 60 ] : def fitjnodels ( data ): О
mfit = {model : models [ model ] fit ( data [ cols bin ] ,
data [ ' Направление ' ] )
for model in models . keysQ}

.

_

_

In [ 61] : fit models ( data )

__

In [ 62 ] : def derive positions ( data ) : ©
for model in models keys ( ) :
data [ ' pos ' + model ] = models [ model ] .
predict ( data [ cols bin ] )

.

_

_

_

In [ 63 ] : derive positions( data )

О Функция, отвечающая за обучение всех моделей.

© Функция, определяющая значения всех позиций для обученных моделей.
Теперь можно переходить к векторизованному тестированию полученных
торговых стратегий. Графики их доходностей приведены на рис. 15.12.

_

©

In [ 64 ] : def evaluate strats ( data ) :
global sel

sel = [ ]
for model in models keys ( ) :
col = ' strat ' + model
data [ col ] = data [ ' pos ' + model ] * data [ ' Доходность ' ]
sel append ( col )
sel . insert ( 0 , ' Доходность ' )

_

.

_

.

_

In [ 65 ] : evaluate strats ( data )

_

.

In [ 66 ] : sel insert ( l , ' strat freq ' )

.

In [ 67 ] : data [ sel ] sum ( ) . apply ( np . exp )
0 ut [ 67 ] : Доходность
0.810644
strat freq
0.989513
strat log reg
1.243322
strat gauss nb 1.243322
strat svm
0.989513
dtype : float 64

©

.

.

_
_
_

_

__

.

In [ 68 ] : data [ sel ] cumsum ( ) . apply ( np exp ) plot ( figsize = ( 10 , 6 ) ) ;

Торговые стратегии

587

О Функция оценки всех торговых стратегий.

© Некоторые стратегии приводят к одинаковым результатам.
1.4

1.3

Доходность
stratfreq
stratlogreg
stratgaussnb
strat svm

1.2

1.1

i .o

0.9

ШМ m }
*

U

0.8

0.7

n>

D3

ON

Date



(VО

.оЬ

'

т>

О'&

т>

О'

п>

л>

^

О'.0

(VО'

тР0>

.о''-

'

Рис. 18.2. Смоделированные траектории прыжковой диффузии

Диффузия по закону квадратного корня



Третий тип рассматриваемых нами случайных процессов диффузия по за кону квадратного корня, предложенная Коксом, Ингерсоллом и Россом [ 2] для
моделирования краткосрочных ставок. Соответствующее стохастическое дифференциальное уравнение имеет следующий вид (см. также уравнение 12.4).
Уравнение 18.5. Стохастическое дифференциальное уравнение для диффу зии по закону квадратного корня

dxt = к [ в xt ) dt + G y[xldZt .
-

Мы применим схему дискретизации, представленную уравнением 18.6
( см. уравнение 12.5, а также уравнение 12.6, описывающее альтернативную,
более точную схему).
Уравнение 18.6. Метод Эйлера для дискретизации модели диффузии по
закону квадратного корня

=\+ k { e

684

Глава 18

~

xs ) ( tm - tm ) +
+]

y

/t m

+

,

Класс моделирования

.

_

_

Ниже показан код класса dx square root diffusion, последнего в этой главе. Помимо, опять-таки, другой модели и схемы дискретизации, класс не содержит ничего нового по сравнению с двумя другими классами моделирования.
Я
Я Пакет DX
Я
Я Класс моделирования - - диффузия по закону квадратного корня
Я
Я square_ root _ diffusion ру
Я
Я Python for Finance , 2 nd ed
Я ( c ) Dr Yves J Hilpisch
Я
import nurnpy as np

.

.

.

.

_
_

from sn__ random_numbers import sn_ random numbers
from simulation class import simulation class

_

_

class square„
diffusion( simulation class ) :
root„

'''

Класс , генерирующий траектории на основе
модели диффузии по закону квадратного корня

Атрибуты
пате : строка
Имя объекта
mar env : экземпляр класса m a r k e t environment
Рыночная среда для моделирования
согг: булево значение
True , если есть корреляция с другим объектом

_

_

Методы

update :
Обновление параметров
generate_ paths :
Возвращает траектории , рассчитанные по методу
Монте - Карло для заданной рыночной среды
г г

t

Финансовое моделирование

685

_

inlt (self, папе, nar env, corr=False):
super(square root diffusion, self). Inlt (папе, nar env,

def

_

# Требуются

_

_

corr)

дополнительные параметры

__

_

_

self.kappa = nar env.get constant('kappa ')
self.theta = nar env.get constant('theta )
1

_

_

def update(self, lnitial value=None, volatlllty= None,
kappa=None, theta=None, flnal date=None):
if initial value is not None:
self.initial value = initial value
if volatility is not None:
self.volatility = volatility
if kappa is not None:
seif.kappa = kappa
if theta is not None:
self.theta = theta
if final date is not None:
self.final date = final date
self.instrunent values = None

_

_

_

_ _
_
_
def generatejpaths(self fixed _seed =True, day_count=
if self.tine_grid is None:
_ _

365.):

,

self.generate tine grid()
M = len(self.tine grid)
I = self.paths
paths = np.zeros((M, I))
paths = np.zeros like(paths)
paths[0] = self.initial value
paths [8] = self.initial value
if self.correlated is False:

_

_

_

_

_

rand
else:
rand

_

=

_

_

_

_

sn randon nunbers((l, M, I),
fixed seed =fixed seed)

= seif.randon_nunbers

_

_

_

for t in range(l, len(self.tine grid)):
dt = (self.tine grid[t] - self.tine grid[t
day_count

-

l]
).days

if self.correlated is False:
ran = rand[t]
else:
ran = np.dot(self.cholesky natrix, rand[:, t, :])
ran = ran[self.rn set]

_

686

Глава 18

_

/

# Метод Эйлера

_

.

( paths_ [ t - :i ] + self kappa *
( self . theta - np . maximum ( 0 ,
paths [ t - 1 , : ] ) ) * dt +
np . sqrt ( np . maximum ( 0 , paths _[ t - 1 , : ] ) ) *
self . volatility * np . sqrt ( dt ) * ran )
paths [ t ] = np . maximum ( 0 , paths _ [ t ] )
self . instrument _ values = paths

paths [ t ]

=

_

Два дополнительных элемента рыночной среды , специфичных для данного
класса, описаны в табл . 18.3.

.

Таблица 18.3 Дополнительные элементы рыночной среды для класса
dx . squore_ root _ diffusion
Элемент

Тип

Обязательный

kappa

Константа

Да

Коэффициент возврата к среднему

theta

Константа

Да

Долгосрочное среднее процесса

Описание

Пример использования
Ниже приведен короткий пример использования класса dx . square _ root _
diffusion . Как обычно , сначала нужно создать объект рыночной среды для
моделирования волатильности .
In [ 56 ] : me _srd

= market _environment ( ' me _ srd

1

,

dt . datetime ( 2020 , 1 , 1 ) )

О

In [ 57 ] : me _ srd . add _constant ( ' initial _value ' , . 25 )
me srd . add _constant ( ' volatility ' , 0.05 )
me srd . add constant ( ' final _date ' , dt . datetime ( 2020 , 12 , 31 ) )
ne _ srd . add _ constant ( ' currency ' , ' EUR ' )
me srd . add constant ( ' frequency ' , ' W ' )
ne _ srd . add _constant ( ' paths ' , 10060 )

_
_

_

_

_

_
_

_

In [ 58 ] : me srd . add _constant ( ' kappa ' , 4.0 )
me srd . add constant ( ' theta ' , 0 . 2 )

_

In [ 59 ] : me srd . add _curve ( ' discount _ curve ' ,
constant _ short _ rate ( ' г ' , 0.0 ) )

_

_

©

_

In [ 60 ] : from square root diffusion import square root diffusion

Финансовое моделирование

687

=

In [ 61] : srd

_

_

_

_

©

square root diffusion ( 1 srd ' , me srd )

In [ 62 ] : srd paths

=

_instrupient_values ( ) [ : ,

srd . get

©

:10 ]

О Дополнительные параметры рыночной среды для объекта dx . square
root _diffusion

_

_

© Объект discount cu г ve требуется по умолчанию, но не используется в
моделировании.
© Создание объекта модели.

© Моделирование траекторий и выбор 10 из них.
На рис. 18.3 показано, как выглядит возврат к среднему. Моделируемые
траектории стремятся к уровню долгосрочного среднего theta (пунктирная
линия ), равного 0,2.
In [ 63] : plt . figure ( figsize= ( 10 , 6 ) )
plt plot ( srd time grid , srd get instrunent values ( ) [ : , : 10 ] )
pit axhline ( me srd get constant ( ' theta ' ) , color = ' r ' ,
ls = 1 - - 1 , lw= 2.0 )
plt . xticks ( rotation = 38 ) ;

.
.

.

_ _

_ .

_

. _

0.25

0.24

0.23

0.22

0.21

0.20

0.1 У

0.18
4
O'o

XoT

x0ЛЬ

X*

0 SP

Xax^

'

O'CP

0’

XoT

'
'O

Xo*

Puc. 18.3. Смоделированные траектории диффузии no закону
квадратного корня ( пунктирной линией обозначен уровень
долгосрочного среднего theta)



688

Глава18

У

пУ

Резюме
В этой главе мы разработали классы, применяемые для моделирования трех
случайных процессов: геометрического броуновского движения, прыжковой
диффузии и диффузии по закону квадратного корня. Сначала мы написали
вспомогательную функцию, генерирующую случайные числа со стандартным
нормальным распределением. Затем мы создали общий класс моделирования,
на основе которого реализовали три специальных класса и показали примеры
их использования.
Чтобы упростить последующий импорт модулей, мы создадим новый оболочечный модуль dx simulation.py.

_

# Пакет DX

#
# Функции и
П

классы моделирования

_

.

# dx sinulation py
#
# Python for Finance , 2nd ed .
# ( c ) Dr Yves J Hilpisch

.

.

#

import numpy as np

import pandas as pd

from
from
from
from
from
from

__
_

_

dx frame import *
sn randomjiumbers import sn random numbers
simulation class import simulation class
geometric„brownian motion import geometric brownian motion
jump diffusion import jump diffusion
square root diffusion import square root diffusion

_

__

_

_ _

_

_

_

_

_

_

_

Как и в случае первого оболочечного модуля, dxjrame.py, подключение
всех необходимых библиотек можно будет выполнять с помощью единствен ной команды импорта:

__

from dx simulation import *

_

Поскольку модуль dx simulation.py импортирует все содержимое модуля dx frame.py тем самым мы подключаем всю имеющуюся на данный момент функциональность. То же самое справедливо для обновленного модуля
in.it, .ру, хранящегося в папке dx.

_

)

Финансовое моделирование

689

ft

it Пакен DX
it
it Пакетный файл
it
ру
init
it

.

#
import numpy as пр
import pandas as pd
import datetime as dt
it Базовые инструменты
from get ..year deltas import get year




constant _short _ rate

from

from market __environment

_deltas
import constant_short _ rate
import market _environment

it Моделирование

from
from
from
from
from

_

_

_

_
_

sn random numbers import sn _ random _ numbers
simulation_class import simulation class
geometricjbrownianjnotion import geometric_ brownian _motion
jumpjjiffusion import jump_diffusion
square _ root _diffusion import square _ root _diffusion

Дополнительные ресурсы
В главе упоминались следующие источники .

.

1 Black, Fischer, and Myron Scholes. The Pricing of Options and Corporate
Liabilities ( 1973, Journal of Political Economy, Vol. 81, No. 3, pp. 637-654).

2. Cox, John, Jonathan Ingersoll, and Stephen Ross. A Theory of the Term
Structure of Interest Rates ( 1985, Econometrica, Vol. 53, No. 2, pp. 385-407 ) .
3. Glasserman, Paul. Monte Carlo Methods in Financial Engineering ( 2004,
Springer ).

.
5. Merton,

4 Hilpisch, Yves. Derivatives Analytics with Python ( 2015, Wiley).

Robert. Theory of Rational Option Pricing ( 1973, Bell
Economics and Management Sciencey Vol. 4, pp. 141-183).

Journal of

6. Merton, Robert. Option Pricing When the Underlying Stock Returns Are
Discontinuous (1976, Journal of Financial Economics, Vol. 3, No. 3, pp. 125-144).

690

Глава 18

ГЛАВА 19

Оценка деривативов
Деривативы



это огромная , сложная проблема.
Джадд Грегг

В течение длительного времени оценкой опционов и других деривативов
занимались только аналитики Уолл -стрит люди с ученой степенью в обла сти точных наук, предполагающих углубленное знание математики. Однако
для практического применения моделей, описываемых численными методами
наподобие метода Монте- Карло, требуется намного меньше знаний, чем для
разработки самих моделей.
Это особенно справедливо в отношении европейских опционов, которые могут погашаться только в конкретную дату, и в меньшей степени в отношении
американских опционов, которые могут погашаться в любой момент на протя жении определенного периода. В этой главе мы рассмотрим метод наименьших
квадратов Монте- Карло ( Least -Squares Monte Carlo — LSM ) эталонный ал горитм оценки американских опционов, основанный на методе Монте- Карло.
Эта глава организована примерно так же, как и предыдущая. Сначала мы
познакомимся с общим классом оценки деривативов, а затем рассмотрим два
специализированных класса для европейского и американского опционов.
Общий класс содержит методы для численной оценки наиболее важных греческих коэффициентов опциона: дельты и веги. Таким образом, эти классы
важны не только для задач прогнозирования стоимости ценных бумаг, но и
для управления рисками.
В главе рассматриваются следующие темы.









Общий класс оценки деривативов
В этом разделе описан общий класс оценки деривативов, от которого
наследуются все остальные классы.

Европейский опцион
В этом разделе рассматривается класс оценки европейских опционов.
Американский опцион
В этом разделе рассматривается класс оценки американских опционов.

Общий класс оценки деривативов
Как и в случае общего класса моделирования, конструктору класса оценки
необходимо передать несколько параметров (в данном случае четыре).

папе
Имя объекта модели,

underlying

Экземпляр класса моделирования, представляющий базовый актив.
mar _env
Экземпляр класса dx market environment

_

.

payoff _ func

Строка, представляющая функцию выплат по опциону.
Общий класс оценки включает три метода.
updateQ
Обновляет выбранные параметры (атрибуты).

deltaQ
Вычисляет дельту опциона.

vegaQ
Вычисляет вегу опциона.

Поскольку из предыдущей главы вы уже знаете о том, как устроен пакет
DX, вы легко разберетесь в структуре общего класса оценки. Везде, где необходимо, предоставлены поясняющие комментарии. Сначала приводится код
класса целиком, а затем мы разберем ключевые нюансы.
Я
Я Пакет DX
Я
Я Оценка деривативов - - базовый класс
Я
Я valuation_ class ру
Я
Я Python for Finance, 2 nd ed
Я ( c ) Dr Yves J Hilpisch
Я

.

.

.

_

.

class valuefcion ciass(object ) :
' ' ' Базовый класс однофакпорной оценки .
692

Глава 19

Атрибуты

пате: строка
Имя объекта
underlying: экземпляр класса моделирования
Объект, моделирующий одиночный фактор риска
mar env: экземпляр класса market environment
Рыночная среда для оценки дериватива
payoffJune: строка
Выплаты по деривативу в синтаксисе Python.
Пример: 'np.maximum(maturity value 100, 0)',
где maturity value NumPy вектор соответствующих
стоимостей базового актива.
Пример: 'np.maximum(instrument values 100, 0)',
где instrument values NumPy матрица стоимостей
базового актива за все время/интервал моделирования.

_

_

_

_ -

-

_

_

-

-

-

Методы

update:
Обновление выбранных параметров
delta:
Возвращает дельту дериватива
vega:
Возвращает вегу дериватива
111

def

_

_

init (self, name, underlying , mar env, payoff func=''):

_

_

self.name = name
self.pricing date = mar env. pricing date

_

try:

Необязательный параметр
self.strike = mar env.get constant('strike')
except:
#

_

_

pass
self.maturity
self.currency

=
=

___

_
_

mar env.get constant(' maturity ')
mar env.get constant('currency ')
# Параметры моделирования и кривая дисконтирования
self.frequency = underlying.frequency
self.paths = underlying.paths
self.discount curve = underlying.discount curve
self.payoff func = payoff func

_

_

_

_

Оценка деривативов

693

self.underlying = underlying
Передача параметров pricing date
и maturity базовому активу
self.underlying.special dates.extend([self.pricing date,
self.maturity])

_

#
#

_

_

_

def update(self, initial value=None, volatility=None,
strike=None, maturity= None):
if initial value is not None:
self.underlying.update(initial value=initial value)
if volatility is not None:
self.underlying.update(volatility=volatility)
if strike is not None:
self.strike = strike
if maturity is not None:
self.maturity = maturity
# Добавляем новую дату исполнения, если она
# отсутствует в сетке time grid
if maturity not in self.underlying.time grid:
self.underlying.special dates.append(maturity)
self.underlying.instrument values = None

_

_

_

_

_

_

_

def delta(self, interval=None, accuracy=4):
if interval is None:
interval = self.underlying.initial value / 58.

_

# Метод

конечных разностей

Вычисление значения слева в формуле дельты
value left = self.present value(fixed seed=True)
# Стоимость базового актива для значения справа
initial del = self.underlying.initial value + interval
self.underlying.update(initial value=initial del)
# Вычисление значения справа в формуле дельты
value right = self ,present value(fixed seed =True)
# Сброс параметра initial value моделируемого объекта
self.underlying.update(initial value=initial del interval)
delta = (value right - valuejleft) / interval
# Поправка на случай возможных ошибок аппроксимации
if delta < - 1.0:
return - 1.0
elif delta > 1.0:
return 1.0
else:
#

_

_

__

_

_

_

694

Глава 19

_

__ _

_

_

_

_ -

return round ( delta , accuracy )

def vega ( setf , interval =0.01, accuracy = 4 ) :
if interval < self . underlying volatility / 50 :
interval = seif underlying volatility / 50
# Метод конечных разностей

.

.

.

.

.

if Вычисление значения слева в формуле веги
value left = self present value ( fixed seed = True )
if Волатильность для значения справа
vola del = self underlying . volatility + interval
if Обновление моделируемого объекта
self underlying update( volatility =vola del )
if Вычисление значения справа в формуле веги
value right = self present value ( fixed seed = True )
И Сброс параметра v o l a t i l i t y моделируемого объекта
self . underlying . update( volatility = vola del - interval )
vega = ( value right - value left ) / interval
return round ( vega , accuracy )

_

_

.

_

_

.
.

.

_

_

_

.

_

_

_
_

_



Одна из задач, решаемых с помощью класса dx . valuation class, оцен ка “ греков ” опциона. Этот вопрос следует рассмотреть подробнее. Предположим, что дисконтированная стоимость опциона описывается непрерывно
дифференцируемой функцией y ( S0 , ^о ) - Тогда дельта опциона определяется как первая частная производная этой функции по текущей стоимости базодП)
вого актива S0, или А =
ds0
Предположим далее, что для стоимости опциона имеется численная оценка
V ( 50 , сг 0 ) , полученная по методу Монте - Карло ( см. главу 12 ). Тогда числен ная аппроксимация дельты опциона описывается уравнением 19.11. Для решения этой задачи в класс оценки добавлен метод delta ( ) . В нем предполагается
существование метода present value ( ) , который возвращает оценку по методу Монте- Карло для заданного набора параметров.

_

• Детали численной оценки “ греков”

по методу Монте- Карло приведены у Глассермана [ 2 ].
В коде реализуется только схема с правыми разностями, так как в этом случае требуется вы полнить всего одно дополнительное моделирование опциона с последующей переоценкой.
Например, в случае схемы с центральными разностями потребуется выполнить сразу две
дополнительные переоценки опциона, что приведет к увеличению вычислительной нагрузки.

Оценка деривативов

695

Уравнение 19.1. Численная аппроксимация дельты опциона
AS
Аналогичным образом рассчитывается вега опциона. Этот коэффициент
определяется как первая частная производная дисконтированной стоимости

. Если имеется оценка стоимости опциона, полученная по методу Монте - Карло, то числен ная аппроксимация веги опциона описывается уравнением 19.2. Для решения
этой задачи в класс dx . valuation class добавлен метод vegaQ .

_

Уравнение 19.2. Численная аппроксимация веги опциона

y_

v ( s , a0 + A ) -V ( S , Сто ) , Аст 0.
>
0

(j

0

А
MSE
0.000 - - > 728.375

Out [ 35 ] : 728.3752973715275

Оценка на основе рыночных данных

749

О Получение прогнозируемых стоимостей.

© Выбор рыночных котировок.
© Поэлементный расчет разницы между прогнозируемыми стоимостями и
рыночными котировками.

© Вычисление среднеквадратической ошибки.
© Пример расчетов.
В главе 11 рассматривались функции spo . bruteQ и spo . fminQ , которые
мы применим для калибровки модели. Сначала выполним глобальную мини мизацию на основе диапазонов для четырех параметров модели. Мы получим
оптимальную комбинацию параметров, вычисленную путем перебора методом грубой силы.
In

[36]:

import scipy .optimize as spo

In [37]: %% time
i = 8
opt global

_

=

_

_

spo . brute ( mean squared error ,

((0.10, 0.201, 0.025),
# диапазон

волатильности

(0.10, 0.80, 0.10),
# диапазон интенсивности скачков

(-0.40, G.01, 0.10),

# диапазон средней величины скачка
( 0.00 , 0.121 , 0.02 ) ) ,
# диапазон вариативности скачков

finish= None )
i
0
75
150
225
300
375
450
525
600
675
750
825
750

Глава 21

vola lambda
0.100
0.100
0.100
0.100
0.125
0.125
0.125
0.150
0.150
0.150
0.175
0.175

0.100
0.300
0.500
0.700
0.200
0.400
0.600
0.100
0.400
0.600
0.100
0.300

mu
- 0.400
- 0.400
- 0.300
- 0.200
- 0.200
- 0.100
0.000
0.000
- 0.400
- 0.300
- 0.200
- 0.200

delta - - > MSE
0.000
0.080
0.040
0.000
0.100
0.060
0.020
0.120
0.080
0.040
0.000
0.100

- - > 728.375
5157.513
- - > 12199.386
- - > 6904.932
- - > 855.412
- - > 621.800
- - > 544.137
- - > 3410.776
- - > 46775.769
- - > 56331.321
- - > 14562.213
- - > 24599.738

- ->

900 0.175
0.500 - 0.100 0.060 - - >
975 0.175
0.700
0.000 0.020 - - >
0.200
0.000 0.120 - - >
1050 0.200
0.500 - 0.400 0.080 - - >
1125 0.200
1200 0.200
0.700 - 0.300 0.040 - - >
CPU tines : user Inin 45s , sys : 7.07 s ,
Wall tine: Inin 56 s

_

_

19183.167
11871.683
31736.403
130372.718
126365.140
total : Inin 52s

_

In [38]: nean squared error ( opt global )
Out [ 38]: 17.946670038040985

_

Значения opt global представляют собой лишь промежуточные результаты. Они используются в качестве начальных значений в задаче локальной
минимизации. А вот значения opt local являются финальными и оптимальными для заданных уровней допуска.

_

In

[39]: %% tine
i = 0
opt local

_

=

_

_

_

spo . fnin ( nean squared error , opt global ,
Xtol=0.00001, ftol=0.00001,
naxiter 200 , naxfun = 550 )

=

i vola lanbda
пи delta - - > MSE
0.200 - 0.300
0.000 - - > 17.947
0 0.100
75 0.098
0.216 - 0.302 - 0.001 - - > 7.885
0.216 - 0.300 - 0.001 - - > 7.371
150 0.098
Optinization terninated successfully
Current function value : 7.371163
Iterations : 100
Function evaluations : 188
CPU tines : user 15.6 s , sys : 1.03 s , total : 16.6 s
Wall tine : 16.7 s

.

In [40]: i

=

_
0

_

_

nean squared error ( opt local ) О
i
nu delta
vola lanbda
0

0.098

0.216

- - > MSE

- 0.300 - 0.001 - - >

7.371

Out [ 40 ] : 7.371162645265256

_ _

_

In [41]: calculate nodel values ( opt local )
0 ut [ 41] : {12050.0 : 647.428189 ,
12100.0 : 607.402796 ,
12150.0 : 568.46137 ,

©

Оценка на основе рыночных данных

751

12200.0 :
12250.0 :
12300.0 :
12350.0 :
12400.0 :
12450.0 :
12500.0 :
12550.0 :
12600.0 :
12650.0 :
12700.0 :
12750.0 :
12800.0 :
12850.0 :
12900.0 :
12950.0 :
13000.0 :

530.703659 ,
494.093839 ,
458.718401 ,
424.650128 ,
392.023241 ,
360.728543 ,
330.727256 ,
302.117223 ,
274.98474 ,
249.501807 ,
225.678695 ,
203.490065 ,
182.947468 ,
163.907583 ,
146.259349 ,
129.909743 ,
114.852425 }

О Среднеквадратическая ошибка при оптимальных значениях параметров.

© Прогнозируемые стоимости при оптимальных значениях параметров.
Далее мы сравним прогнозируемые стоимости при оптимальных значени ях параметров с рыночными котировками. Ошибки оценки вычисляются как
абсолютные разницы между спрогнозированными и рыночными значениями,
а также как процентные отклонения от рыночных котировок.
In [ 42 ] : option _ selection[' MODEL'] = np.array(list(
calculate _ model_ values(opt _local).valuesQ ))
option _ selection[' ERRORS _ EUR '] = (option _ selection[
' MODEL'] - option _ selection[ ' CF _ CL0SE ' ])
option _ selection[' ERR0RS _%'] = (option _ selection[
' ERRORS _ EUR '] / option _ selection['CF _ CL0SE']) * 100
In [ 43] : option _ selection [[ ' MODEL ' , ' CF CLOSE ' , ' ERR 0RS _
' ERRORS. % • ]]
Out[ 43] :
MODEL CF.
ERR0RS %
.CLOSE ERR0RS EUR
43 647.428189
642.6
4.828189
0.751352
45 607.402796
604.4
3.002796
0.496823
1.361370
47 568.461370
0.240058
567.1
49 530.703659
0.303659
530.4
0.057251
- 0.706161 - 0.142716
51 494.093839
494.8
- 1.581599 - 0.343602
53 458.718401
460.3

_

752

Глава 21

_

_

55 424.650128
57 392.023241
59 360.728543
330.727256
302.117223
274.984740
249.501807
225.678695
203.490065
182.947468
163.907583
146.259349
129.909743
114.852425

61
63
65
67
69
71
73
75
77
79
81

- 2.149872 - 0.503719
- 2.376759 - 0.602627
- 2.571457 - 0.707805

426.8
394.4
363.3
333.3
304.8
277.5
251.7
227.3
204.1
182.4
162.0
142.9
125.4
109.4

- 0.771900
- 2.682777 - 0.880176
- 2.572744

- 2.515260
- 2.198193
- 1.621305
- 0.609935

- 0.906400
- 0.873338
- 0.713289
- 0.298841

0.547468
1.907583
3.359349
4.509743
5.452425

0.300147
1.177520
2.350839
3.596286
4.983935

_

_

_

_ .

О

In [ 44 ] : round ( option selection [ ' ERRORS EUR ' ] . mean ( ) , 3 )
Out [ 44 ] : 0.184
In [ 45 ] : round ( option selection [ ' ERR 0 RS % ' ] nean ( ) , 3 )
0 ut [ 45 ] : 0.36

©

О Средняя ошибка оценки в евро.
Q

Средняя ошибка оценки в процентах.
Результаты оценки и ошибки оценки представлены на рис. 21.4.

In [ 46 ] : fix , ( axl , ах 2 , ахЗ )

.

plt subplots ( 3 , sharex = True ,
figsize= ( 10 , 10 ) )
strikes = option selection [ ' STRIKE PRC ' ] values
axl . plot ( strikes , option selection [ ' CF CL0SE ' ] ,
label = 1 Биржевые котировки ' )
axl plot ( strikes , option selection [ ' MODEL ' ] , ' r o ' ,
label = ' Прогнозируемая стоимость ' )
axl . set ylabel ( ' Стоимость опциона ' )
axl legend ( loc = 0 )
wi = 15
ax 2 . bar ( strikes - wi / 2 . , option selection [ ' ERR 0RS EUR ' ] ,
width = wi )
ax 2 set ylabel ( ' Ошибка , E U R ' )
ax 3 . bar ( strikes - wi / 2 . , option selection [ ' ERR 0RS % ' ] ,
width = wi )
ахЗ set ylabel ( Ошибка , % ' )
ахЗ . set xlabel ( ' Цена исполнения ' ) ;

_

.
.
.

=

_
_

_

_

.

_
_

. _

_

_

_

_

1

_

Оценка на основе рыночных данных

753



600

cd
^



К

|s 500

Биржевые котировки
Прогнозируемая стоимость

о

g

300

К

£

200
100

В!

4

£

ш

0

(

2

М

I

о

\

к

а

о

0

I

"

1 1 1 1 1 1

'

I

-2

5
4

£

я
Ьн
о

\

К

а

о

3
2

1

I

0

I

.

-1
12000

в

12200

12400
12600
Цена исполнения

12800

13000

Рис . 21.4 . Сравнение прогнозируемых стоимостей и рыночных котировок

754

после калибровки модели
Скорость калибровки
Полноценная калибровка модели оценки опционов по рыночным
данным требует пересчета сотен или даже тысяч значений. Именно поэтому она обычно основывается на аналитических формулах
ценообразования. В данном случае калибровка выполняется по
методу Монте- Карло, которые намного требовательнее к производительности вычислительной системы, чем аналитические методы.
Тем не менее процедура калибровки не длится “ бесконечно долго”
даже при запуске на обычном ноутбуке. Ее скорость можно существенно повысить за счет ряда методик, таких как векторизация и
распараллеливание вычислений.

Глава 21

Оценка портфеля
Получив в свое распоряжение откалиброванную модель, отражающую реалии финансовых рынков, которые представлены данными о котировках лик видно торгуемых опционов, можно приступать к моделированию и оценке
неторгуемых опционов. Идея заключается в том, что процедура калибровки
равнозначна добавлению в модель корректной риск- нейтральной мартингальной меры благодаря настройке оптимальных параметров. На основе этой меры
мы можем применить фундаментальную теорему ценообразования финансовых активов к остальным опционам, которые не участвовали в калибровке.
В этом разделе мы рассмотрим портфель американских пут-опционов ин декса DAX. На биржах нет ликвидно торгуемых опционов такого типа. Для
простоты будем считать, что американские пут -опционы имеют те же даты исполнения, что и европейские колл -опционы, использованные для калибровки
модели. Также предполагается, что у них одинаковые страйк- цены.

Моделирование опционных позиций
Прежде всего смоделируем рыночную среду для базового фактора риска —
с оптимальными параметрами, полученными в
фондового индекса DAX
.
результате калибровки
In [ 47 ] : me_dax = rnarket _environment ( ' me _dax ' , pricing _date )
me_dax . add _constant ( ' initial _value ' , initial _ value )
me_dax . add _constant ( ' final_date ' , pricing _date )
me_dax . add _constant ( ' currency ' , ' EUR ' )



_ . _constant ( ' volatility ' , opt _local[8] )
_
( ' lambda , opt _local [ l ] ) О
_ . __constant
constant ( ' mu ' , opt _local [ 2 ] ) О

In [ 48 ] : me dax add
me dax . add
me dax add
me dax add

_
_

.

In [ 49 ] : me dax . add

О

О

1

_constant
_constant

_

( ' delta ' , opt local [ 3 ] )

О

( ' model ' , ' j d ' )

Добавление оптимальных параметров, полученных в результате кали бровки модели.

Далее определим опционные позиции и соответствующие им рыночные
среды, сохранив их в двух разных словарях.
In [ 50 ] : payoff _ func
In [ 51] : shared

= ' np . maximum ( strike - instrunent _values , 0 ) '

= market _environment ( ' share ' ,

_

prlcing date ) 0

Оценка на основе рыночных данных

755

.

_

shared add constant ( ' maturity ' , maturity )
shared. add_constant ( ' currency ' , ' EUR ' ) О

О

In [ 52 ] : option_ positions = { }
option_environments = {}
for option in option_selection index :

.

option_environments [ option ] = market _environment (
' am_put _%d ' % option, pricing date ) ©
strike = option_selection[ ' STRIKE_ PRC ' ] loc [ option ]
option_environments [ option ] add constant ( 1 strike ' ,
strike ) ©
option environments [ option ] add environment ( shared )
option positions [ am_put _%d ' % strike ] = \
derivatives _position (
' am_put _%d ' % strike,
quantity=np random randint ( 10, 50 ) ,
underlying= ' dax _model ' ,
mar _ env=option environments [ option ] ,
otype= ' American ' ,
payoff _ func =payoff _ func ) ©

_

.

. _

_
_

.

_

©
©

1

.

.

_

О Общий объект dx .market _environment, который послужит базисом
для рыночных сред всех опционов.

© Новый объект dx .market _environment для релевантного американского пут- опциона.

© Добавление параметра страйк -цены опциона.

.

О Добавление элементов из общего объекта dx market _environment в
объект рыночной среды конкретного опциона.

© Объект dx . derivatives _position с произвольным количеством
опционов.

Портфель опционов
Для оценки портфеля американских пут -опционов нам нужна отдельная
рыночная среда. Она будет включать основные параметры для расчета стои-

мости позиций и сбора статистики.

= market _environment ( ' val_env ' , pricing_date )
_ . add_constant ( ' starting_date ' , pricing_date )
. add_constant ( ' final_date ' , pricing_date) ©
_ . add_curve( ' discount _curve ' , csr )

In [ 53 ] : val_ env
val env
val_env
val env

756

Глава 21

__

__

val env.add constant(' frequency ' , ' В')
val env.add constant(' paths ' , 25800)

= {'dax_nodel'

In [54]: underlyings
In [55]: portfolio

=

: ne_dax}

_

©

_

derivatives portfolio(' portfolio',
option positions, val env, underlyings)

_

_

©

_

In [56]: %tine results = portfolio.get statistics(fixed seed =True)
CPU tines: user Inin 5s, sys: 2.91 s, total: Inin 8s
Wall tine: 38.2 s
In [57]: results.round(l)
Out[57]:
name quant.
0 an put 1205O
33
1 an put 12100
38
2 an put 12150
20
3 an put 12200
12
4
5
6
7
8
9
10

11
12
13
14
15
16
17
18
19

0
1
2
3
4
5
6

__ __
_ __
_
an_put_
an_ put_
an_ put_
an_ put_1240O
an_ put_12450
an_ _12500
an_ put_12550
an_put_
an_ put_12650
an_ put_
an_ put_12750
an_ put_12800
an_ put_12850
an_ put_12900
an_put_
an_ put_13000
12250
12300
12350

put

12600
12700

12950

37
37
36
16
17
16
38
10
38
40
13
49
30
33
40
35

value curr. pos _ value \
EUR
5002.8
EUR
6138.4
EUR
3426.8
EUR
2206.6
EUR
7302.8
EUR
7853.9
EUR
8224.1
EUR
3908.4
EUR
4465.6
EUR
4534.8
11602.3
EUR
EUR
3303.9
EUR
13508.3
EUR
15367.5
EUR
5375.7
EUR
21806.6
EUR
14321.8
EUR
16840.1
EUR
21777.0
EUR
20378.9

151.6
161.5
171.3
183.9
197.4
212.3
228.4
244.3
262.7
283.4
305.3
330.4
355.5
384.2
413.5
445.0
477.4
510.3
544.4
582.3

pos_ delta pos
vega
.
4.7 38206.9
5.7 51365.2
3.3 27894.5
2.2 18479.7
7.3 59423.5
8.2 65911.9
9.0 70969.4

-

Оценка на основе рыночных данных

757

- 4.3
- 5.1
- 5.2

7
8
9
10
11
12
13
14
15
16
17
18
19

- 13.3

- 3.9
- 16.0
- 18.6

- 6.5
- 26.3
- 17.0
- 19.7
- 24.9
- 22.9

32871.4
37451.2
36158.2
86869.9
22144.5
89124.8
90871.2
28626.0
105287.3
60757.2
69163.6
80472.3
66522.6

_

In [ SB ] : results [[ ' pos _ value ' , ' pos _delta ' , ' pos vega ' ]] . sum ( ) . round ( l )
197346.2
Out[ SB ] : pos value
pos _delta
- 224.0
pos vega
1138571.1
dtype : float 64

_
_

О Параметр final _date впоследствии сбрасывается к финальной дате исполнения для всех опционов портфеля.

© Все американские пут -опционы портфеля привязаны к одному и тому же
фактору риска



фондовому индексу DAX.

© Создание объекта dx . derivatives_ portfolio.
Расчет статистических показателей займет какое-то время, поскольку он
выполняется по методу наименьших квадратов Монте- Карло, который ока зывается особенно трудоемким в случае американских опционов. Так как мы
имеем дело только с длинными позициями американских пут-опционов, рассматриваемый портфель характеризуется маленькой дельтой и большой вегой.

Код Python
Ниже показан код Python , предназначенный для получения данных опци онов фондового индекса DAX с помощью программного интерфейса Eikon.
In

[1]: Import eikon as ek О
import pandas as pd
import datetime as dt
import configparser as cp

758

Глава 21

.

©

I n [ 2 ] : cfg = cp ConfigParserQ

.

.

c f g read( ' eikon c f g ' )
Out [ 2 ] : [ ' eikon c f g ' ]

.

©

.

I n [ 3 ] : ek set _ app _i d ( c f g [ ' eikon ' ] [ ' app_i d ' ] )

_

©

I n [ 4 ] : f i e l d s = [ ' CF DATE ' , ' EXPIR _DATE 1 , ' PUTCALLIND ' ,
' STRIKE PRC ' , ' CF _CL0SE ' , ' IMP_V0LT ' ]

.

_

_

©

.

I n [ 5 ] : dax = ek g e t data ( ' 0# GDAXN8 * EX ' , f i e l d s= f i e l d s ) [ 0 ]
I n [ 6 ] : dax

.infoQ

©

©

.

.

.


Rangelndex : 115 entries , 0 t o 114
Data columns ( t o t a l 7 columns ) :
115 non - n u l l object
Instrument
CF _DATE
115 non - n u l l object
EXPIR _DATE
114 non - n u l l object
PUTCALLIND
114 non - n u l l object
STRIKE PRC
114 non - n u l l float 64
CF CL0SE
115 non - n u l l f l o a t 64
IMP V0LT
114 non - n u l l f l o a t 64
dtypes : f l o a t 64 ( 3 ) , object ( 4 )
memory usage: 6 . 4+ KB

_

__

_

.

I n [ 7 ] : dax [ ' Instrument ' ] = dax [ ' Instrument ' ] apply (
lambda x: x replace( ' / ' , ' ' ) ) ©

.

. _

.

I n [ 8 ] : dax set index ( ' Instrument ' ) head ( 10 )
Out [ 8 ] :
CF_DATE EXPIR _DATE
Instrument
. GDAXI 2018 - 04 - 27
None
GDAX105OO0G8 . EX 2018 - 04 - 27 2018 - 07 - 20
GDAX10500OS8 EX 2018 - 04 - 27 2018 - 07 - 20
GDAX1080OOG8 . EX 2018 - 04 - 27 2018 - 07 - 20
GDAX1O8000S8 EX 2018 - 04 - 26 2018 - 07 - 20
GDAX 110000G 8 . EX 2018 - 04 - 27 2018 - 07 - 20
GDAX 110000S 8 . EX 2018 - 04 - 27 2018 - 07 - 20
GDAX 111500G 8 EX 2018 - 04 - 27 2018 - 07 - 20

PUTCALLIND

.

None
CALL
PUT
CALL
PUT

.

CALL
PUT
CALL

.

Оценка на основе рыночных данных

759

GDAX111500S8.ЕХ 2018 - 04 - 27 2018 - 07 - 20
GDAX112000G8.ЕХ 2018 - 04 - 27 2018 - 07- 20

Instrument
.GDAXI
GDAX105000G8.ЕХ
GDAX105000S8.ЕХ
GDAX108000G8.ЕХ
GDAX108000S8.ЕХ
GDAX110000G8.ЕХ
GDAX110000S8.ЕХ
GDAX111500G8.ЕХ
GDAX111500S8.ЕХ
GDAX112000G8.ЕХ

In [ 9 ] : dax.to _ csv(

PUT
CALL

STRIKE_ PRC

CF
CLOSE
.

IMP_ V0LT

NaN
10500.0
10500.0
10800.0
10800.0
11000.0
11000.0
11150.0
11150.0
11200.0

12500.47
2040.80
32.00
1752.40
43.80
1562.80
54.50
1422.50
64.30
1376.10

NaN
23.59
23.59
22.02
22.02
21.00
21.00
20.24
20.25
19.99

/source/ tr _ eikon _option _ data.csv ')

©

О Импорт оболочечного пакета eikon .

© Считывание учетных данных для программного интерфейса Eikon.

0 Определение извлекаемых полей.
О Получение данных опционов с экспирацией в июле 2018 года.

0 Замена символов косой черты ( / ' ) в названиях инструментов.
1

© Запись набора данных в CSV-файл.

Резюме
В этой главе мы рассмотрели более крупный и реалистичный пример использования аналитического пакета DX для оценки портфеля неторгуемых
американских опционов немецкого фондового индекса DAX. В любом приложении, связанном с анализом деривативов, приходится решать три основные
задачи.

Получение данных
Для моделирования и оценки деривативов требуются актуальные ры ночные данные. В нашем случае мы использовали данные индекса DAX
и котировки его опционов.

760

Глава 21

Калибровка модели
Чтобы иметь возможность оценивать и хеджировать неторгуемые опци оны рыночным способом, необходимо откалибровать параметры используемой модели (объекта моделирования ) по соответствующим рыночным
котировкам опционов (которые должны быть релевантны по срокам исполнения и страйк - ценам ). Нами была выбрана модель прыжковой диффузии, которая в некоторых случаях достаточно хорошо описывает поведение фондовых индексов. Результаты калибровки оказались вполне
удовлетворительными, несмотря на то что модель характеризуется только
тремя степенями свободы ( lambda интенсивность скачка, mu ожидаемый размер скачка, delta вариативность размера скачка).







Оценка портфеля

Собрав рыночные данные и получив откалиброванную модель, мы смоделировали портфель американских пут -опционов индекса DAX и рассчитали его основные статистические показатели ( стоимости позиций,
значения дельты и веги ).
Этот пример призван продемонстрировать гибкость и функциональность
пакета DX, который позволяет решать все основные задачи, связанные с ана лизом деривативов. Используемый подход и архитектура приложения во многом сопоставимы с эталонным случаем применения аналитической формулы
Блэка Шоулза Мертона для европейских опционов. Как только объекты
оценки определены, их можно использовать примерно так же, как и аналитическую формулу, даже несмотря на то, что для численных расчетов в них
применяются вычислительно сложные и ресурсоемкие алгоритмы.





Дополнительные ресурсы
Как и в предыдущих главах, хорошим справочным руководством по теме
главы (особенно что касается калибровки модели оценки опционов ) послужит книга Хилпиша [ 2].

.

1 Black, Fischer, and Myron Scholes. The Pricing of Options and Corporate
Liabilities ( 1973, Journal of Political Economy, Vol. 81, No. 3, pp. 637-654).
2. Hilpisch, Yves. Derivatives Analytics with Python ( 2015, Wiley).

3. Merton, Robert. Theory of Rational Option Pricing ( 1973, Bell
Economics and Management Science, Vol. 4, pp. 141-183).

Journal of

См. также список литературы в конце главы 20.
Оценка на основе рыночных данных

761

ЧАСТЬ VI

Приложения

ПРИЛОЖЕНИЕ А

Обработка значений даты и времени
Значения даты и времени играют важную роль в финансовых расчетах.
В этом приложении мы рассмотрим различные способы работы с ними в коде
Python. Конечно, данное описание не является исчерпывающим. Задача при познакомить читателя с основными компонентами экосистемы
ложения
,
Python которые поддерживают обработку значений даты и времени.



Python
Модуль datetime ( https : / / docs . python . org / 3 / library / datetime . html )
стандартной библиотеки Python позволяет выполнять большинство основных операций со значениями даты и времени.
In [ 1 ] : from pylab import mpl , pit
pit . style . use ( ' seaborn ' )
mpl . rcParams [ ' font . family ' ]
%matplotlib inline

= ' serif '

In [ 2 ] : import datetime as dt
In [ 3 ] : dt . datetime . now ( ) О
0ut [ 3 ] : datetime . datetime ( 2018 , 10 , 19 , 15 , 17 , 32 , 164295 )
In [ 4 ] : to = dt . datetime . today ( ) О
to
0ut [ 4 ] : datetime . datetime ( 2018 , 10 , 19 , 15 , 17 , 32 , 177092 )
In [ 5 ] : type ( to )
0ut[ 5 ] : datetime . datetime
In [ 6 ] : dt . datetime . today ( ) . weekdayO ©
0ut [ 6 ] : 4

О Возвращает точную дату и системное время.

© Возвращает день недели в виде порядкового числа, где 0 соответствует
понедельнику.

Объекты datetime можно создавать напрямую.

= dt . datetime ( 2020 , 10 , 31 , 10 , 5 , 30 , 500000 ) О
d
Out[ 7 ] : datetime . datetime ( 2020 , 10 , 31 , 10 , 5 , 30 , 508000 )
In [ 7 ] : d

In [8 ] : str ( d ) ©
0ut [ 8 ] : ' 2020 - 10 - 31 10 : 05 : 30.500000 '
In [ 9 ] : print ( d ) ©
2020 - 10 - 31 10 : 05 : 30.500000

0

In [10 ] : d . year
Out[10 ] : 2020

©

In [11] : d . month
Out [ 11] : 10

©

In [ 12 ] : d . day
Out [ 12 ] : 31
In [ 13] : d . h
Out [ 13] : 10

©

О Пользовательский объект datetime .
© Строковое представление даты и времени.

© Вывод объекта на экран.
О Атрибуты year ( год)...
( месяц )...

0

...month

©

.. . day (день) ...

© ...и hour ( час) объекта datetime .
Над объектом datetime можно выполнять различные преобразования.
In [14 ] : о
о

766

= d . toordinal ( ) ©

Приложение А

Out [14 ] : 737729

In [ 15 ] : dt . datetime . fromordinal ( o ) ©
Out [ 15 ] : datetime . datetime ( 202O , 10 , 31 , 0 , 0 )
In [ 16 ] : t

= dt . datetime . time ( d ) ©

t
0ut[ 16 ] : datetime . time ( 10 , 5 , 30 , 500000 )

In [17 ] : type ( t )
Out [17 ] : datetime . time
In [ 18] : dd = dt . datetime . date ( d )

0

dd
Out [ lS ] : datetime . date ( 2020 , 10 , 31 )
In [19 ] : d . replace ( second = 0 , microsecond = 0 ) ©
0 ut [ 19 ] : datetime . datetime ( 2020 , 10 , 31 , 10 , 5 )

О Преобразование в порядковое число.

© Преобразование из порядкового числа.
© Выделение компонента time (время ).

О Выделение компонента date ( дата).

© Установка заданных значений в 0.
При выполнении определенных арифметических операций с объектами
datetime ( например, вычисление разницы между двумя датами ) результирующие значения могут иметь тип timedelta .

= d - dt . datetime . now ( ) О
td
Out [ 20 ] : datetime . timedelta ( days = 742 , seconds = 67678 ,
In [ 20 ] : td

microseconds = 169720 )
In [ 21] : type ( td ) ©
0ut [ 21 ] : datetime . timedelta

In [ 22 ] : td . days
Out [ 22 ] : 742
In [ 23 ] : td . seconds

Обработка значений даты и времени

767

Out [ 23 ] : 67678

.

In [ 24 ] : td microseconds
Out [ 24 ] : 169720

.

In [ 25 ] : td total_ seconds ( )
Out [ 25 ] : 64176478.16972

©

О Объект разности между двумя объектами datetime...

© имеет тип timedelta.
© Разница между двумя датами в секундах.
Объект datetime можно преобразовывать в различные представления.
Кроме того, можно создать такой объект из строки. Детали приведены в документации к модулю datetime. Рассмотрим несколько примеров.

.

In [ 26 ] : d isoformatQ О
Out [ 26 ] : ' 2020 - 10 - 31Т10: 05 : 30.500000 '

.

.

In [ 27 ] : d strftime ( ' %A, %d %B %Y %1:Шр ' )
Out [ 27 ] : ' Saturday , 31 October 2020 10: 05 AM '

.

.

©

.

In [ 28 ] : dt datetime strptime ( ' 2017 - 03 - 31 ' , ' 0/oY - %m - %d ' )
0ut [ 2S ] : datetime datetime ( 2017, 3, 31, 0, 0 )

.

.

.

©

In [ 29 ] : dt datetime strptime ( ' 30 - 4 - 16 ' , ' %d - %m - % y ' )
0ut [ 29 ]: datetime datetime ( 2016, 4, 30, 0, 0)

.

©

In [ 30 ] : ds = str ( d )

Out [ 30 ] :

ds
' 2020 - 10 - 31 10 : 05 : 30.500000 '

.

.

.

In [ 31] : dt datetime strptime ( ds, ' %Y - %m - %d %H: %M: %S % f ' ) ©
0ut [ 31] : datetime datetime ( 2020, 10, 31, 10, 5, 30, 500000 )

.

О Строковое представление в формате ISO.

© Представление даты в виде строкового шаблона.
© Создание объекта datetime на основе строкового шаблона.
Помимо функций now ( ) и today ( ) поддерживается также функция
utcnow ( ) , которая возвращает точную информацию о дате и времени в форма те UTC ( Coordinated Universal Time
всемирное координированное время ) ,



768

Приложение А

ранее известном как GMT (Greenwich Mean Time — среднее время по Гринвичу). Оно может отличаться на несколько часов от местного часового пояса.

.

.

In [ 32 ] : dt datetine nowO
Out [ 32 ]: datetime datetime ( 2018, 10, 19, 15, 17, 32, 438889 )

.

.

.

In [ 33 ] : dt datetime utcnowQ О
Out [ 33 ] : datetime datetime ( 2018, 10, 19, 13, 17, 32, 448897 )

.

.

.

.

.

In [ 34 ] : dt datetime now ( ) - dt datetime utcnow ( ) ©
0ut [ 34 ] : datetime timedelta ( seconds=7199, microseconds=999995 )

.

О Возвращает текущее время по UTC.

© Разница между местным временем и текущим временем по UTC.
В модуле datetime содержится общий класс часового пояса tzinfo, включающий методы utcoffsetQ, dst ( ) и tzname ( ). На его основе можно, к
примеру, создать классы, управляющие временем по UTC и CEST (Central
European Summer Time — центральноевропейское летнее время).

.

In [ 35 ] : class UTC ( dt tzinfo) :

def utcoffset(self, d ) :
return dt . timedelta ( hours=0 )
def dst(self, d ) :
return dt . timedelta ( hours=0 )
def tzname(self, d ) :
return

.

In [ 36 ] : и = dt datetime

©
©

' UTC '

.utcnowQ

In [ 37 ] : и
0ut [ 37 ] : datetime datetime ( 2018, 10, 19, 13, 17, 32, 474585 )

.

.

In [ 38 ] : и = и replace ( tzinfo=UTC ( ) )

©

In [ 39 ] : и
0ut [ 39 ] : datetime datetime ( 2018, 10, 19, 13, 17, 32, 474585,
tzinfo=< main
UTC object at
Oxllc 9 a 2320> )

.

.

.

In [ 40 ] : class CE 5 T ( dt tzinfo) :

def

utcoffset(self, d ) :

.

return dt timedelta ( hours = 2 )

©

Обработка значений даты и времени

769

def dst($elf, d ) :
return dt . timedelta ( hours = l ) ©
def tzname ( self , d ) :
return 'CEST'
In [41]: c

= u . astimezone ( CEST ( ) ) ©

c
0ut[ 41 ] : datetime . datetime ( 2018 , 10 , 19 , 15 , 17 , 32 , 474585 ,
tzinfo= < main . CEST object at
0 xllc 9 a 2ccO > )
In [42]: c - c . dstQ ©
Out[42]: datetime . datetime ( 2018 , 10 , 19 , 14 , 17 , 32 , 474585 ,
tzinfo = < main . CEST object at
0xllc 9 a 2cc0> )

О Нет смещения от UTC.

© Подключение объекта dt . tzinfo с помощью метода гер1асе ( ) .
© Смещение для стандартного и летнего ( DST
мени по CEST.

— Daylight

Saving Time) вре-

© Переход из часового пояса UTC в часовой пояс CEST.
© Получение летнего времени для преобразованного объекта datetime .
В Python также имеется отдельный модуль pytz , реализующий поддержку
основных часовых поясов.
In [43]: import pytz

In [ 44 ] : pytz . country _ names [ ' US ' ]
0ut[ 44 ] : ' United States '

©

In [ 45 ] : pytz . country _ timezones [ ' BE ' ]
0ut[ 45 ] : [ ' Europe / Brussels ' ]
In [ 46 ] : pytz . common _ timezones [ 10:]
0 ut [ 46 ] : [ ' Pacific / Wake ' ,
' Pacific / Wallis ' ,
' US / Alaska ' ,
' US / Arizona ' ,
' US / Central ' ,
' US / Eastern ' ,

770

Приложение A

©
©

' US /Hawaii ' ,
' US / Mountain ' ,
' US / Pacific ' ,
' UTC ' ]
О Название страны.

© Отдельный часовой пояс.
@

Несколько часовых поясов.

Благодаря модулю pytz нет необходимости создавать пользовательские
объекты tzinfo.

.

In [ 47 ] : u = dt datetime

.utcnowQ

.

.

In [ 48 ] : и = u replace ( tzinfo=pytz utc )

О

In [ 49 ] : и
0ut [ 49 ] : datetime datetime ( 2018, 10, 19, 13, 17, 32, 611417,
tzinfo= )

.

.

.

In [ 50 ] : u astimezone ( pytz timezone ( ' CET ' ) ) ©
Out [ 50 ] : datetime datetime ( 2018, 10, 19, 15 , 17, 32, 611417,
tzinfo= )

.

.

.

In [ 51] : u astimezone ( pytz timezone ( ' GMT ' )) ©
0ut [ 51] : datetime datetime ( 2018, 10, 19, 13, 17, 32, 611417,
tzinfo= )

.

.

.

In [ 52 ] : u astimezone ( pytz timezone( ' US / Central ' ) ) ©
Out [ 52 ] : datetime datetime ( 2018, 10, 19, 8, 17, 32, 611417,
tzinfo= )

.

О Определение часового пояса с помощью модуля pytz.

© Преобразование объекта datetime в разные часовые пояса.

NumPy
Библиотека NumPy содержит собственные программные инструменты для
работы со значениями даты и времени.
Обработка значений даты и времени

771

In [ S 3 ] : import nunpy as np

.

In [ 54 ] : nd = np datetime 64( ' 2020 - 10 - 31 ' )

nd

О

.

Out [ 54 ] : numpy datetime 64( 12020 - 10 - 31' )

.

In [ 55 ] : np datetime_ as _ string( nd )
Out [ 55 ] : ' 2020 - 10 - 31 '

.

In [ 56 ] : np datetime_ data ( nd )
Out [ 56 ] : ( ' D ' , 1)

О

©

In [ 57 ] : d
0ut [ S 7 ] : datetime datetime( 2020, 10, 31, 10, 5, 30, 500000 )

.

.

In [ 58 ] : nd = np datetime 64 ( d )

nd

©

.

Out [ 58 ] : numpy datetime 64( ' 2020 - 10 - 31T10: 05 : 30.500000 ' )

.

.

In [ 59 ] : nd astype ( dt datetime ) ©
0ut [ 59 ] : datetime datetime( 2020, 10, 31, 10, 5, 30, 500000 )

.

О Создание объекта даты из строки и получение строкового представления
даты.

© Метаинформация об объекте даты (тип, размер).
© Создание объекта даты на основе объекта datetime.
© Преобразование в объект datetime.
Другой способ создания объекта даты заключается в предоставлении строкового шаблона, включающего, например, год, месяц и частоту повторения.
Обратите внимание на то, что по умолчанию значение такого объекта задается равным первому дню месяца. Также можно создавать массивы дат на осно ве списков.

.

In [ 60 ] : nd = np datetime64( ' 2020 - 10 ' , ' D ' )

nd

.

Out [ 60 ] : numpy datetime 64 ( ' 2020 - 10 - 01 ' )

.

.

In [ 61] : np datetime64 ( 12020 - 10 ' ) == np datetime64 ( ' 2020 - 10 - 01' )
Out [ 61] : True

772

Приложение A

In [62]: np.array(['2020 - 06 - 10 ', ' 2020 - 07 - 10', '2020 - 08 - 10'],
dtype='datetime64' )
Out[62]: array(['2020 - 06 - 10', '2020 - 07 - 10', '2020 - 08 - 10'],
dtype ='datetine64[D]')
In [63]: np.array(['2020 - 06 - 10112:00:00', ' 2020 -07 - 10112:00:00',
'2020 - 08 - 10T12:00:00 ' ], dtype= 'datetime64[s]')
0ut[63]: array(['2020 - 06 - 10112:00:00', '2020 - 07 - 10112:00:00',
'2O 20 - 08 - lOT12:0O:0O '], dtype= ' datetime64[s]' )

Функция np.arange() позволяет генерировать диапазоны дат. В ней можно указывать частоту повторения (например, дни, недели или секунды).
In [64]: np.arange('2020 - 01 - 01 ', '2020 - 01 - 04 ', dtype= ' datetime64 ' )
0ut[64]: аггау([ ' 2020 - 01 - 01 ', '2020 - 01 -02 ', ' 2020 - 01 - 03'],
dtype =' datetime64[D]')

О

In [65]: np.arange( ' 2020 - 01 - 01 ', '2020 - 10 - 01 ',
dtype = 'datetine64[M ]') ©
0ut[65]: array(['2020 - 01 ', '2020 - 02', '2020 - 03', '2020 - 04',
'2020 - 05 ', '2020 - 06', '2020 - 07', '2020 - 08',
'2020 - 09'], dtype= 'datetime64[ M]')
In [66]: np.arange('2020 - 01 - 01', '2020 - 10 - 01',
dtype='datetime64[W]' )[:10] ©
0ut[66]: array(['2019 - 12 - 26', '2020 - 01 - 02', '2020 - 01 - 09',
'2020 - 01 - 16', '2020 - 01 - 23', '2020 - 01 - 30',
'2020 - 02 - 06 ', '2020 - 02 - 13 ', '2020 - 02 - 20',
'2020 - 02 - 27 ' ], dtype= 'datetime64[W]')
In [67]: dtl = np.arange('2020 - 01 - 01100:00:00',
'2020 - 01 - 02100:00:00',
dtype= ' datetime64[h]')

©

dtl[ : 10 ]
0ut[67]: аггау(['2020 -01 - 01T00', ' 2020 - 01 - 01T01', '2020 - 01 - 01T02',
'2020 - 01 - O1T03', '2020 - 01 - 01T04', '2020 - 01 - 01T05',
'2020 - 01 - 01T06', '2020 - 01 - 01T07', '2020 - 01 - 01T08',
'2020 - 01 - O1T09'], dtype= 'datetine64[h]')

In [68]: np.arange('2020 - 01 - 01100:00:00', '2020 - 01 - 02100:00:00',
dtype='datetine64[s]')[:10] ©
0ut[68]: array([ ' 2020 - 01 - 01100:00:00', '2020 - 01 - 01100:00:01 ',
'2020 - 01 - 01T00:00:02', '2020 - 01 - 01T00:00:03',
' 2020 - 01 - 01T00:O0:04', '2020 - 01 - 01100:00:05',
Обработка значений даты и времени

773

' 2020 - 01 - Q1T00 : 00 : 06 ' , ' 2020 - 01 - 01100 : 00 : 07 ' ,
' 2020 - 01 - 01T00 : 00 : 08 ' , ' 2020 - 01 - 01T 00 : 00 : 09 ' ] ,
dtype = ' datetime 64 [ s ] ' )

-

In [ 69 ] : np . arange ( ' 2020 - 01 - 01100 : 00 : 00 ' , ' 2020 01- 02100 : 00 : 00 ' ,
dtype = ' datetime 64 [ ms ] ' ) [ : 10 ] ©
Out [ 69 ] : array ( [ ' 2020 - 01- 01100 : 00: 00.000 ' , ' 2020 - 01- 01T0O : 00 : 00.001 ' ,
' 2020 - 01- 01T00 : 00 : 00.002 ' , ' 2020 - 01- 01T00 : 00 : 00.003 ' ,
' 2020 - 01- 01100 : 00 : 00.004 ' , ' 2020 - 01- 01T00 : O0 : 0O . 005 ' ,
' 2020 - 01- 01TO0 : 00 : 00.006 ' , ' 2020 - 01- 01T00 : 00 : 00.007 ' ,
' 2020 - 01 - 01T00 : 00 : 00.008 ' , ' 2020 01- 01TO0 : 00 : 00.009 ' ] ,
dtype = ' datetime64[ ms ] ' )

-

О Ежедневное повторение.
0 Ежемесячное повторение.
0 Еженедельное повторение.
О Повторение через час.

0 Повторение через секунду.

© Повторение через миллисекунду.
Построение графиков временных рядов иногда оказывается непростой задачей . В библиотеке matplotlib реализована поддержка стандартных объектов datetime, поэтому обычно достаточно преобразовать объект datetime64
библиотеки NumPy в объект datetime стандартной библиотеки Python, как
показано в следующем примере ( рис. А.1).
In [ 70 ] : import matplotlib . pyplot as pit
%matplotlib inline
In [ 71] : np . random . seed ( 3000 )

rnd

=

_

.

np random . standard normal ( len ( dtl ) ) . cumsumQ ** 2

In [ 72 ] : fig = pit , figure ( figsize= ( 10 , 6 ) )
pit . plot ( dtl astype ( dt datetime ) , rnd )
fig . autofmt xdate ( ) ; ©

_

.

.

О

О Значения x представляются объектами datetime.

© Автоматическое форматирование подписей оси X в виде значений даты /
времени.

774

Приложение А

200
175

150
1ZJ

25

о4

ЛЧ

. Дч
^

%

^

v\

. г4

0^
0%
^
^
Рис. А. 1 . График с автоматически отформатированными подписями
оси Ху которые представлены объектами datetime
\

0

0

0

0

pandas
Библиотека pandas изначально разрабатывалась в расчете на поддержку
временных рядов (см. официальную документацию по адресу http : / / bit .
ly / timeseries_doc ) , поэтому она включает классы, предназначенные для эффективной обработки значений даты и времени, например Datetimelndex .
В библиотеке pandas также имеется объект Tines tamp, который служит
альтернативой объектам datetime и datetime64.
In [ 73 ] : import pandas as pd
In [ 74 ] : t s

= pd . TimestampC ' 2020 - 06 - 30 ' )
ts
Out [ 74 ] : TimestampC 2020 - 06 - 30 00 : 00 : 00 ' )
In [ 75 ] : d

=

_

ts . to pydatetime ( )

О

©

d
0ut[ 75 ] : datetime . datetime ( 202O , 6 , 30 , 0 , 0 )
In [ 76 ] : pd . Timestamp ( d ) ©
0ut [ 76 ] : TimestampC 12020 - 06 - 30 00 : 00 : 00 ' )

Обработка значений даты и времени

775

In [77]: pd .Timestamp(nd) О
Out[77]: Timestamp('2020 10 01 00:00:00')

- -

О Создание объекта Timestamp из строки.
© Создание объекта datetime из объекта Timestamp.
© Создание объекта Timestamp из объекта datetime.
© Создание объекта Timestamp из объекта datetime64.
Вышеупомянутый класс Datetimelndex (http:// bit.ly/datetimeindex _
doc) представляет собой коллекцию объектов Timestamp, снабженную рядом
полезных методов. Объект Datetimelndex можно создать с помощью функ ции pd.date_ range()(http:// bit.ly /date _ range _ doc; см. главу 5), которая
позволяет легко строить индексы временных рядов. Ниже приведено несколько примеров преобразований дат.
In [78]: dti = pd.date range('2020/01/01', freq = ' M ' , periods=12) ©

_

dti
Out[78]: Datetimelndex(['2020 -01 - 31', '2020 02- 29', '2020 03 - 31',

- - -

-

-

- - 31', '2020-06 - 30',
- -31'', ''2020-09-30'',
- -30 , 2020- 12-31 ],

'2020 04 30' , '2020 05
'2020 07 31' , '2020 08
'2020 10 - 31 ' , '2020 11
dtype='datetime64[ns]',

freq =' M')

In [79]: dti[6]
Out[79]: Timestamp(' 2020 -07- 31 00:00:00', freq ='M ')

_

In [80]: pdi = dti.to pydatetime() ©
pdi
Out[80]: array([datetime.datetime(2020,
datetime.datetime(2020,
datetime.datetime(2020,
datetime.datetime(2020,
datetime.datetime(2020,
datetime.datetime(2020,
datetime.datetime(2020,
datetime.datetime(2020,
datetime.datetime(2020,
datetime.datetime(2020,
datetime.datetime(2020,
datetime.datetime(2020,
dtype=object)

776

Приложение A

. 31, 0, 0),

i
2,
3,
4,
5,
6,
7,
8,
9,
10,

.

11
12,

29,
31,
30,
31,
30,
31,
31,
30,
31,
30,
31,

0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,

0),
0),
0),
0),
0),
0),
0),
0),
0),
0),
0)],

In [81]: pd.Datetimelndex(pdi) ©
Out[81]: Datetimelndex(['2020-01- 31

- - 29'' , ''2020-03-31' ,
- -31', '2020-06-30'',
- -31 , 2020-09-30 ,
- -30', '2020-12-31']

2020 02
'2020 04 30
2020 05
' 2020 07 31
2020 08
' 2020 10 31
2020 11
dtype= ' datetime64[ns]',

- - - -

freq =None)

In [82]: pd.Datetimelndex(dtl) ©
Out[82]: Datetimelndex([' 2020 01 01 00:00:00', '2020 01 01 01:00:00',
'2020 01 01 02:00:00', '2020 01 01 03:00:00' ,
'2020 01 01 04:00:00', '2020 01 01 05:00:00',

- - - - - - ' 2020 - 01 - 01 06:00:00', '2020-01-01 07:00:00',
'2020 - 01 - 01 08:00:00', ' 2020-01-01 09:00:00 ’ ,
' 2020 - 01 -01 10:00:00', '2020-01- 01 11:00:00' ,
'2020- 01 - 01 12:00:00', ' 2020-01-01 13:00:00',
'2020 - 01 - 01 14:00:00', '2020 - 01-01 15:00:00' ,
'2020 - 01 - 01 16:00:00', '2020 - 01 -01 17:00:00' ,
'2020 -01- 01 18:00:00', '2020-01-01 19:00:00' ,
'2020-01 - 01 20:00:00', '2020- 01- 01 21:00:00',
'2020-01-01 22:00:00', '2020 - 01-01 23:00:00']

dtype='datetime64[ns]', freq =None)

О Объект Datetimelndex, содержащий 12 периодов с ежемесячной частотой повторения.
© Преобразование объекта Datetimelndex в массив ndarray, содержащий
объекты datetime.

© Создание объекта Datetimelndex на основе массива ndarray, содержащего объекты datetime.

© Создание объекта Datetimelndex на основе массива ndarray, содержащего объекты datetime64.

Библиотека pandas позволяет строить наглядные графики временных за висимостей ( рис. А.2). Дополнительные примеры были приведены в главе 8.
In [83]: rnd

In [84]: df

= np.random.standard_normal(len(dti)).cumsumQ

** 2

= pd.DataFrame(rnd , columns=['Данные '], index=dti)

In [85]: df.plot(figsize=(18, 6));

Обработка значений даты и времени

777

Данные

30

25

20

15

10

5

0

Jan

Feb

Mar

Apr

May

Jun

Jul

Aug

Sep

Oct

Nov

Dec

2020

Рис. A .2 . Построенный библиотекой pandas график с автоматически
отформатированными подписями оси X , которые представлены
объектами Timestamp

Библиотека pandas хорошо интегрируется с модулем pytz при работе с ча-

совыми поясами.

In [86]: pd.date _ range( ' 2020/01/01', freq =' M ', periods= 12,
tz= pytz.timezone( CET'))
0ut[86]: Datetimelndex(['2020 - 01 - 31 00:00:00+01:00',
'2020 - 02 - 29 00:00:00+01:00', '2020 - 03 - 31 00:00:00+02:00',
'2020 - 04- 30 00:00:00+02:00', '2020 - 05 - 3100:00:00+02:00',
'2020 - 06 - 30 00:00:00+02:00 ', ' 2020 - 07 - 31 00:00:00+02:00',
'2020 - 08 - 31 00:00:00+02:00', ' 2020-09 - 30 00:00:00+02:00 ’,
'2020 - 10 - 31 00:00:00+01:00', ' 2020 - 11 - 30 00:00:00+01:00',
'2020 - 12 - 31 00:00:00+01:00'], dtype= 'datetime64[ ns, CET]',
freq ='M ')
1

In [87]: dti = pd.date_ range('2020/01/01', freq =' M ', pertods = 12,
tz = ' US/ Eastern ')
dti
0ut[87]: Datetimelndex([' 2020 - 01 - 31 00:00:00 - 05:00',
'2020 - 02 - 29 00:00:00 - 05:00 ’, ' 2020 - 03 - 31 00:00:00 - 04:00',
'2020 - 04- 30 00:00:00- 04:00', '2020 - 05 - 31 00:00:00- 04:00',
'2020 - 06 - 30 00:00:00 - 04:00 ', '2020 - 07 - 31 00:00:00 - 04:00 ',
'2020 - 08 - 31 00:00:00 - 04:00 ', ' 2020 - 09 - 30 00:00:00 - 04:00 ',
778

Приложение A

- - -

-

- -

-

'2020 10 31 00:00:00 04:00' , '2020 11 30 00:00:00 05:00',
' 2020 12 31 00:00:00 05:00'],
dtype= ' datetime64[ns, US/Eastern]', freq = ' M ')

_

In [88]: dti.tz convert(' GMT')
0ut[88]: Datetimelndex([' 2020 01 31 05:00:00+00:00' ,
'2020 02 29 05:00:00+00:00', '2020 03 31 04:00:00+00:00',
'2020 04 30 04:00:00+00:00' , '2020 05 31 04:00:00+00:00 ’ ,
'2020 06 30 04:00:00+00:00', ' 2020 07 31 04:00:00+00:00',
'2020 08 31 04:00:00+00:00', ' 2020 09 30 04:00:00+00:00' ,
'2020 10 31 04:00:00+00:00', ' 2020 11 30 05:00:00+00:00' ,
'2020 12 31 05:00:00+00:00'], dtype= ' datetime64[ns, GMT]',
freq='M')

-

-

- -

-

-

Обработка значений даты и времени

779

ПРИЛОЖЕНИЕ Б

Класс опционов в модели
Блэка — Шоулза — Мертона

Определение класса
Ниже приведено определение класса для европейского колл-опциона в модели Блэка — Шоулза — Мертона. Подход на основе класса можно считать альтер нативой реализации с помощью функций, которая рассматривалась в главе 12.
#
# Оценка европейских колл - опционов в модели

#
#
#
#
#
#
#

Блэка - Шоулза - Мертона ( BSM ) , включая функции
вычисления веги и ожидаемой волатильности
Реализация на основе класса

.

.

Python
( c ) Dr

for Finance, 2nd ed .

.

Yves J

.

Hilpisch

from math import log, sqrt , exp
from scipv import stats

__

__

class bsm call option(object):
' ' 1 Класс оценки европейских колл - опционов
в модели Блэка

- Шоулза -

.

Мертона

Атрибуты

SO :

float
Начальный уровень акции / индекса

К:

float

Т:

float

Страйк - цена
Срок исполнения ( в долях года )

r:

float

Постоянная безрисковая краткосрочная ставка

signa: float

Коэффициент волатильности компонента диффузии

Методы

value:
Возвращает текущую стоимость колл опциона
vega:
Возвращает вегу колл - опциона
imp _ vol:
Возвращает ожидаемую волатильность

-

1 1 1

def

init (self, SO, К, T, г, signa):
self.SO = float(SO)
self.К = К
self.T = T
self.г = г
self.signa = signa

def value(self):
''' Возвращает стоимость опциона
i i i

dl = ((log(self.SO / self.K) +
(self.г + 0.5 * self.signa ** 2) * self.T) /
(self.signa * sqrt(self.T)))
d 2 = ((log(self.SO / self.K) +
(self.г - 0.5 * self.signa ** 2) * self.T) /
(self.signa * sqrt(self,T)))
value = (self.SO * stats.norn.cdf(dl, 0.0, 1.0) self.K * exp( - self.r * self.T) *
stats.norn.cdf(d 2, 0.0, 1.0))
return value
.

def vega(self):

'' ’ Возвращает вегу опциона
t i i

dl = ((log(self.SO / self.K) +
(self.г + 0, 5 * self.signa ** 2) * self.T) /
(self.signa * sqrt(self.T)))
vega = self.SO * stats.norn.pdf(dl, 0.0, 1.0) * sqrt(self.T)
return vega

782

Приложение Б

_

def imp vol( self, C0, sigma_est =8.2, it=lQ8 ) :
' ' ' Возвращает ожидаемую волатильность
для заданной цены опциона
t i l

_

_

.

.

.

.

option = bsm call option( self S 0, self К, self T, self г ,
sigma_est )
for i in range (it ) :
option sigma - = ( option valueQ - CO ) / option vegaQ
return option sigma

.

.

.

.

Пример использования
Созданный класс можно применить в отдельном интерактивном сеансе

Jupyter Notebook.

_

_

In [1] : from bsm option cla$ s import

*

.

In [ 2 ] : о = bsm_call_option( 100 , 105 . , 1.0, 0.05, 0.2 )
type( o )
Out [ 2 ] : bsm_option class bsm_call_option

_

.

.

In [ 3 ] : value = o value ( )

value
0ut [ 3 ] : 8.021352235143176

.

In [ 4 ] : o vegaQ
Out [ 4 ] : 39.67052380842653

.

In [ 5 ] : o imp_vol( CO=value )
0ut [ 5 ] : 0.2

С помощью данного класса можно, например, визуализировать стоимость
и вегу опциона для разных страйк -цен и сроков исполнения. В конце концов,
это одно из главных преимуществ наличия аналитической формулы оценки
опциона. В следующем коде Python генерируются статистические показатели
опциона для разных комбинаций страйк -цен и сроков исполнения.
In [ 6 ] : import numpy as np
maturities = np linspace( 0.05, 2.8, 20 )
strikes = np linspace ( 80, 120, 28 )
T, К = np meshgrid( strikes, maturities )
C = np zeros like ( K )
V = np zeros_like ( C )

.
.

.

.

.

_

Класс опционов в модели Блэка — Шоулза — Мертона

783

for t in enunerate( piaturities ) :
for к in enumerate ( strikes ) :
o T = t [l]
o K = к [1]
C [ t [ 0 ] , k [ 0 ] ] = o value ()
V [ t [ 8 ] , k [ 0 ] ] = o vega ( )

.
.

.
.

Ценовая поверхность европейского колл- опциона показана на рис. Б.1.
In [ 7 ] : from pylab import cm, mpl, pit
from mpl toolkits mplot 3d import Axes 3D
mpl rcParams [ ' font family ' ] = ' serif '
%matplotlib inline

__

.

.

.

.
.

In [ 8 ] : fig = plt figure ( figsize=( 12, 7 ) )
ax = fig gca ( projection= ' 3d ' )
surf = ax plot _ surface( T , К, C, rstride=l, cstride=l,
cmap=cm coolwarm, linewidth=0.5,
antialiased=True )
ax set _ xlabel( ' Страйк - цена ' )
ax set ylabel( ' \ пСрок исполнения \ п( в долях года ) ' )
ax set _ zlabel( ' Стоимость европейского \ пколл - опциона ' )
fig colorbar ( surf , shrink=0.5, aspect=5 );

.

.

.

. _
.
.

h 25
- 20
- 15

- 10

г

-5

2.00

80

1.75
1.50
1.25

85

90

95

ZPa

(

100

1n

**W

. ..

>
^

0.75

_

no

°

'

115

120

0.25
0.00

50

° $V

*

o

Рис Б 1 Ценовая поверхность европейского колл - опциона

784

Приложение Б

Поверхность значений веги европейского колл-опциона представлена на
рис. Б.2.

.

In [ 9 ] : fig = pit figure( figsize=( 12, 7 ) )
ax = fig gca ( projection= ' 3 d ' )
surf = ax plot surface( T, К, V , rstride=l, cstride=l,
cmap=cm coolwarn, linewidth=0.5,
antialiased=T rue )
ax set xlabel( ' Страйк - цена ' )
ax set ylabel( ' \ пСрок исполнения \ п( в долях года ) ' )
ax set zlabel( ' Вега европейского \ пколл - опциона ' )
fig colorbar ( surf , shrink=0.5, aspect= 5 ) ;

.

__

.

.

. _
. _
. _
.

- 50

-c
S
’5

r

«

- 40

-

- 30

=

a T
30 о e
a
« e?
W

20

£8

CD

- 20
I
-

10

2.00

BO

85

1.75
1.50
1.25