AT industries: Course (Developers)

AT industries: Course (Developers)


Основатели AT industries : alexmak, tim.eth


Введение

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

Все ссылки, приведенные в курсе, являются выжимкой информации, которую мы считаем достаточной для прикладного применения Python.

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

Удачи в прохождении!


Программа курса:

  1. Объектно-ориентированное программирование
  2. Анализ данных (numpy, pandas)
  3. API-запросы (requests)
  4. Графики (plotly)
  5. Асинхронное программирование (asyncio)
  6. Вебсокеты (threading, websocket)
  7. Логирование (logger)
  8. Телеграм (aiogram)
  9. Базы данных (sqlite3)
  10. Web3 (web3)


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

В математике любой объект можно рассмотреть в виде множества. Например:

  1. Число — это множество, но из одного элемента | a := {a}.
  2. Матрица — это множество множеств | A := {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}
  3. Поле — это множество элементов | R := {..., -0.5, ..., 0.0, ..., 0.5, ...}

Так вот, если в математике каждый объект есть множество, то в программировании каждый объект есть класс! А точнее объект некоторого класса!

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

Зачем это нужно?

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

ТЕОРИЯ

плейлист: ссылка

ПРАКТИКА

задание_01:

  1. Написать класс Asset с полями: type(str), name(str), price(float), status(bool), где type - это тип нашего актива (equity/option/cryptocurrency), name - имя нашего актива (SPX/USOIL/BTCUSDT), price - цена нашего актива (36,123/123,0/-12.11), status - статус торговли (True/False).
  2. Написать конструктор класса, который при создании объекта выводит сообщение в терминал о создании данного объекта.
  3. Перегрузить оператор печати "__str__", сделать вывод некоторой информации об объекте данного класса.
  4. Перегрузить оператор сравнения "__lt__". Объекты класса должны сравниваться по значению поля pricе.
  5. Создать массив объектов данного класса и отсортировать по цене.

задание_02:

  1. Написать класс Trade, который является наследником класса Asset и состоит из полей: date(str), quantity(float), side(str), где date - это дата совершения сделки (1707580035.000/1706379357.000), quantity - количество купленного актива (0.0042, 1235.12), side - сторона сделка (buy/sell).
  2. Для класса Trade реализовать метод get_size(), который считает объем позиции совершенной сделки в долларах.
  3. Создать несколько объектов класса Trade с рандомными значениями полей. Для всех объетов класса Trade вызвать метод get_size().


2. Анализ данных (numpy, pandas)

Есть две основные библиотеки для анализа данных в Python: NumPy и Pandas. Мы не будем сильно углубляться в каждую из них, так как не считаем, что стоит тратить много времени на изучение данных библиотек. Однако, поверхностное ознакомление с numpy и pandas явно не будет лишним для Python-разработчика.

Что такое NumPy?

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

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

Что такое Pandas?

Pandas — это библиотека Python для обработки и анализа структурированных панельных данных. Панельными данными называют информацию, полученную в результате исследований в виде таблиц.

То есть, Pandas — это удобная библиотека, для работы с таблицами (иногда очень большого размера).

ТЕОРИЯ

видео_01: ссылка (nupmy)

видео_02: ссылка (pandas)

ПРАКТИКА

задание_01: (nupmy)

Задания: 1, 2, 5, 7, 10, 11, 14, 15

https://colab.research.google.com/drive/1xG9SKOsXwq0wFW5NBtnSw1B8odFcAU0y?usp=sharing

задание_02: (pandas)

Задания: 0, 1, 2, 3, 4, 5, 6, 7

https://colab.research.google.com/drive/1CWFPDleJQdv_eG_yRYXBOs9Aj7-osVAG?usp=sharing


3. API-запросы (requests)

В этом разделе мы научимся создавать и отправлять API-запросы с вашего компьютера на другие сервера. Но для начала, давайте определим, что такое API и API-запросы.

Что такое API?

API (Application Programming Interface) — это набор правил и способов, с помощью которых различные компьютерные программы взаимодействуют друг с другом. Другими словами, API — это язык, на котором программы обмениваются информацией и выполняют различные операции.

Что такое API-запросы?

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

Что такое GET и POST?

Все API-запросы делятся на два типа: GET и POST.

GET — это тип запроса, который не вносит изменений в базу данных на сервере-получателе. Например, чтобы узнать ваш баланс USD на бирже, сервер-получатель просто прочитает значение вашего баланса и отправит ответный JSON с полученной информацией, не изменяя данные в базе данных своих пользователей.

POST — это тип запроса, который привносит изменения в базу данных на сервере-получателе. Например, чтобы отправить заявку на покупку BTC на бирже, сервер-получатель должен взаимодействовать с базой данных как минимум для уменьшения количества USD и увеличения количества BTC на вашем балансе.

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

Например, на биржах криптовалют (и не только) такими паролями (ключами) являются ваши API-ключи, а в блокчейне — seed фразы или приватные ключи от кошелька.

Также хотелось бы добавить, что в блокчейне, особенно в сети Ethereum, GET-запросы являются бесплатными функциями, так как НЕ изменяют состояние блокчейна, их называют "read". С другой стороны, POST-запросы, также называемые "write", платные, так как требуют выполнения транзакций и оплаты комиссий майнерам за изменение состояния блокчейна.

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

документация_01: ссылка (OKX)

документация_02: ссылка (ByBit)

документация_03: ссылка (Binance)

ТЕОРИЯ

видео_01: ссылка

видео_02: ссылка (GET)

видео_03: ссылка (POST)

ПРАКТИКА

Выберите одну из бирж с которой вам удобнее всего работать.

задание_01:

  1. Написать функцию get_current_price(ticker: str), которая будет возвращать текущую цену актива на выбранной вами бирже.
  2. Написать get_decimals(ticker: str), которая будет возвращать количество знаков после запятой у данного актива на выбранной вами бирже.

задание_02:

  1. Написать функцию get_trading_balance(ticker: str), которая будет возвращать ваш баланс актива на выбранной вами бирже.
  2. Написать функцию post_limit_order(ticker: str, amount: float, price: float), которая будет выставлять лимитную заявку на выбранной вами бирже.

задание_03:

  1. Обернуть все вышеперечисленные функции в класс, конструктор которого принимает API ключи пользователя.
  2. Реализовать метод check_connection() для класса, который проверяет подключение к бирже и возвращает булево значение (True/False). Обязательна проверка введенных пользователем API ключей!
  3. Вне класса написать функцию run_mainloop(ticker: str, timeframe: str), которая запускает вечный цикл. Вечный цикл должен присылать информацию по закрытой свече того таймфрейма и того актива, которые подаются на вход функции. Например run_mainloop('BTCUSDT', '1m') будет присылать информацию (price_open, price_close, price_high, price_low, volume, timestamp) для каждой новой закрытой минутной свечи.


4. Графики (plotly)

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

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

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

документация_01: ссылка (Plotly)

ТЕОРИЯ

видео_01: ссылка

ПРАКТИКА

Выберите одну из бирж с которой вам удобнее всего работать.

задание_01:

  1. Написать функцию get_klines_df(ticker: str, timeframe: str, n: int), которая получает информацию о свечах введенного актива в виде датафрейма за последне n закрытых свечей введенного таймфрейма на выбранной вами бирже.
  2. По полученному из первого пункта датафрейму построить свечной график цены актива (смотреть: Candlestick Charts).
  3. На свечной график цены нанести индикатор SMA-10 в виде обычного графика линии. Подписать график индикатора как sma_10.
  4. На свечной график цены нанести горизонтальную линию красного цвета, значение которой равно: (df['high'].max() + df['low'].min()) / 2
  5. Под свечным графиком цены построить график объемов (по желанию в виде столбчатой диаграммы). Важно, чтобы ось времени у обоих графиков была одна и та же, чтобы при увеличении одного графика увеличивался и другой!
  6. Настроить конфиг: убрать ползунки диапазонов, добавить zoom, сделать шкалу значений цены актива логарифмической.
  7. Сохранить полученный график в виде HTML и PNG файлов.


5. Асинхронное программирование (asyncio)

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

Данная концепция не совсем удобна для огромного количества проектов по нескольким причинам:

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

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

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

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

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

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

ТЕОРИЯ

видео_01: ссылка

видео_02: ссылка

плейлист_01: ссылка

ПРАКТИКА

задание_01:

Реализовать первый пример применения асинхронности из указанного выше видео_02: "Python с нуля для криптанов...await, брутфорс кошельков"

задание_02:

Реализовать первый пример применения асинхронности из видео #7 из указанного выше плейлиста: "Асинхронность в Python"

задание_03:

Реализовать второй пример применения асинхронности из видео #7 из указанного выше плейлиста: "Асинхронность в Python"


6. Вебсокеты (threading, websocket)

Помимо асинхронного программирования существует еще одно решение проблем, затронутых в разделе [5] этого курса. Этим решением является внешний модуль Threading.

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

Также, в этом разделе мы разберем работу с вебсокетами в Python.

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

ТЕОРИЯ

статья_01: ссылка (threading)

статья_02: ссылка (websocket)

ПРАКТИКА

Выберите одну из бирж с которой вам удобнее всего работать.

задание_01:

  1. Реализовать примеры из статьи: "Как работать с модулем threading в Python"
  2. Реализовать примеры из статьи: "Как работать с WebSocket в Python"

задание_02:

  1. Написать свой класс Socket, конструктор которого принимает на вход следующие параметры: url и ticker, где url — это ссылка на подключение к вебсокету, ticker — это тикер актива, по которому мы хотим получать информацию. Конструктор дожен сохранять значения url и ticker, а также создавать вебсокет подключение, используя бибоиотеку websocket.
  2. В классе Socket реализовать следующие методы: on_message(), on_open(), on_close(), on_error(). Каждый метод должен выводить сообщение в терминал при вызове, пример: 'Websocket: opened BTCUSDT'
  3. Параллельно запустить вебсокеты для нескольких активов активов, используя библиотеку threading.

задание_03:

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

  1. Дополнить класс Socket, который мы создали во втором задании этого раздела, следующими полями: n и data, где n — размер окна индикатора, который мы будем использовать в стратегии, data — словарь, в котором мы будем сохранять информацию о монете за последние n тиков.
  2. Написать создание словаря data в конструкторе таким образом, чтобы при вызове конструктора нашего класса создавался словарь, ключами которого были: 'datetime' и 'price', а их значениями были массивы из None (кол-ва n).
  3. В методе on_message необходимо распаковать полученную информацию об активе и записать datetime и price в наш словарь data таким образом, чтобы новые элементы массивов вставали на первое место, а самый старые элементы (n+1)ые стирались их массивов. Иными словами, реализовать динамическую очередь.
  4. В методе on_message только после полного заполнения словаря data последней информацией об активе, необходимо рассчитать стандартное отклонение цены актива за последние n тиков.
  5. В методе on_message написть алгоритм стратегии: если цена актива, больше или меньше вычисленного стандартного отклонения — отправить сообщение в терминал о покупке или продаже актива.


7. Логирование (logger)

Ранее мы использовали функцию print() для вывода всех комментариев о работе программы в терминале, и это было довольно эффективно.

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

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

Лог (log) — это текстовый файл, куда автоматически записывается важная информация о работе системы или программы.

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

Мы настоятельно рекомендуем всегда записывать логи!

ТЕОРИЯ

видео_01: ссылка

ПРАКТИКА

задание_01:

  1. Создать отдельный файл logger.py в котором будет храниться глобальная переменная нашего логгера и вся информация о нем.
  2. Создать отдельную папку, в которой будут сохраняться логи нашего запусков программы.
  3. Поменять конфигурацию логера таким образом, чтобы файлы с логами сохранялись в папку, созданную в пункте 2, с названиями даты и временем старта программы ('%Y-%m-%d_%H-%M-%S.log').
  4. Поменять конфигурацию логера таким образом, чтобы его сообщения отображались вот так:
    2024-02-21 19:53:26 | DEB | ...
    2024-02-21 19:53:26 | ERR | ...
  5. Поменять конфигурацию логера таким образом, чтобы во время выполнения программы логи не только записывались в файл, но и выводились в окне терминала.


8. Телеграм (aiogram)

Для взаимодействия с Telegram можно использовать прямые API-запросы. Однако, при работе над крупными проектами удобнее обращаться к сторонним библиотекам, таким как telebot или aiogram.

В чем различие telebot и aiogram?

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

В нашем курсе мы сосредоточимся на использовании библиотеки aiogram.

ТЕОРИЯ

плейлист_01: ссылка (смотреть выборочно)

ПРАКТИКА

Выберите одну из бирж с которой вам удобнее всего работать.

задание_01:

Написать телеграм бота, который принимает ticker актива и присылает в ответ текущую цену данного актива.

задание_02:

Используя машину состояний (смотреть плейлист), написать телеграм бота, который по команде /saveorder принимает подряд следующие значения: type, ticker, price, amount и сохраняет информацию о сделке. По команде /showorders выводит все сделки, занесенные в телеграм бота.

задание_03:

Написать телеграм бота, который с некоторой регулярностью присылает информацию об активах добавленных пользователем по команде /addcoin. Информация должна содержать в себе: тикер актива, цену и объем.


9. Базы данных (sqlite3)

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

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

В нашем курсе мы рассмотрим библиотеку sqlite3 для работы с базами данных используя SQL в Python.

SQL — это отдельный язык программирования, который мы будем использовать для взаимодействия с самой базой данных. То есть, в проектах, написанных на Python, мы будем отправлять SQL-запросы в базу данных для получения или изменения хранимой информации.

ТЕОРИЯ
плейлист_01: ссылка

ПРАКТИКА

Написать API базы данных, которая состоит из таблиц: users и orders, где таблица users — это таблица с данными о пользователе (id, name, balance), а orders — это таблица со сделками пользователей (id, side, ticker, price, amount). В функционал API должны входить следующие методы:

  1. add_user(id: str, name: str, balance: float) — добавление нового пользователя в таблицу users базы данных.
  2. delete_user(id: str) — удаление пользователя из базы данных по id. Удаление пользователя подразумевает удаление данных о пользователе как из таблицы users, так и из таблицы orders!
  3. get_users() — получение словаря, содержащего информацию о всех пользователях из таблицы users базы данных, где ключом словаря является id пользователя, а значением словарь с информацией о пользователе.
  4. add_order(id: str, side: str, ticker: str, price: float, amount: float) — добавление сделки в таблицу orders базы данных. Если пользователя с таким id нет в таблице users, то функция должна вернуть ошибку.
  5. delete_orders(id: str) — удаление всех сделок пользователя с данным id. Удаление сделок пользователя из таблицы orders не подразумевает удаление данных о пользователе из таблицы users!


10. Web3 (web3)

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

Настоятельно рекомендуем прочитать предисловие к разделу [3] данного курса, перед тем как браться за этот раздел. Также, для начального понимания работы блокчейна посмотрите видео_01, приведенное ниже в теории данного раздела.

Как уже упоминалось в разделе [3], у смарт-контракта существует 2 типа функций для взаимодействия с ним: read и write. Запросы read являются бесплатными для использования, write — платными. Поэтому, для выполнения практической части данного раздела, необходимо пополнить баланс вашего EVM-кошелька нативными монетами сетей, которые мы будем использовать.

ТЕОРИЯ

видео_01: ссылка (блокчейн)

видео_02: ссылка (практика)

ПРАКТИКА

В практической части данного раздела мы научимся бриджить USDC между EVM сетями по мостам проекта Wormhole.

задание_01:

Написать собственный класс Client (видео_02) для взаимодействия с блокчейном. Класс должен содержать в себе следующие методы:

  1. is_connected() — проверка подключения к ноде c используемой RPC.
  2. get_balance_native() — получение баланса нативной монеты кошелька.
  3. get_balance_foreign(address_token: str) — получение баланса стороннего токена кошелька, где address_token — адрес смарт-контракта токена.
  4. approve(address_token: str, address_spender: str, amount: int) — одобрение на списывания токено , где address_token — адрес смарт-контракта токена, address_spender — адрес транжира, amount — количесвто токенов.
  5. send_transaction(address_to: str, address_from: str, data: dict, value: float) — отправление транзакции в блокчейн, где address_to — адрес отправителя, address_from — адрес получателя, data — допонительна информация о транзакции, value — количество пересылаемых нативных токенов.

задание_02:

Написать собственный класс Wormhole(client) для работы с мостами проекта Wormhole. В данном классе реализовать один метод:

transfer(

address_token: str, address_bridge: str,

address_from: str, address_to: str,

chain_id_to: int, amount: int,

),

где address_token — адрес смарт-контракта токена, address_bridge — адрес бриджа, address_from — адрес отправителя, address_to — адрес получателя, chain_id_to — айди сети получателя, amount — количество USDC для бриджа.

Данный метод должен бриджить USDC из одной сети в другую используя мосты проекта Wormhole. Пример такой транзакции можно посмотреть тут:

https://snowtrace.io/tx/0x1b655f9641aaf0a3e5da3b2f3672b426fc0aeb73a1e2eb7735013ea5bd904f11

задание_03:

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

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

Поэтому, данное задание состоит в том, чтобы самостоятельно подумать о реализации функци 'completeTransfer' и написать нам о своих результатах и мыслях по этому поводу: tim.eth, alexmak.

Мы дадим некоторые подсказки о том, как все же реализовать данный метод!)

Report Page