Алгоритмическая торговая стратегия

  • Часть-1 (Основная стратегия)
  • Часть-2 (Файлы логирования)
  • Часть-3 (Обновления Telegram-бота)
  • Тонкая настройка алгоритмической стратегии с помощью оптимизации роя частиц
  • Алгоритмическая торговля со стохастическим осциллятором
  • Алгоритмическая торговля с индикатором Aroon в Python
  • Использование дельта-нейтральных стратегий для стабильной безрисковой прибыли
  • Высокочастотная стратегия получения ликвидности

Часть-1 (Основная стратегия)

В этой статье мы сделаем полноценный работающий и автоматизируем скрипт с примером стратегии для выполнения AlgoTrading.

ЗНАКОМСТВО

В любое время можно разместить ордер на покупку или продажу с помощью Kite API. Но AlgoTrading — это все о том, когда размещать эти ордера на основе стратегий и, самое главное, какавтоматизироватьэтот процесс. Используя Python и Selenium, этот процесс может быть полностью автоматизирован и без какого-либо вмешательства человека.

СОДЕРЖАНИЕ

1. Стратегия автоматизации

2. Параметры и файлы учетных данных

3. Автоматизация процесса входа в систему

4. Кодирование стратегии

— 4.1 Интервал выборки высокий и интервальный низкий

— 4.2 Создание условий для входа в сделку.

— 4.3 Создание условий для выхода из сделки.

5. Заключение

1. Стратегия автоматизации

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

Стратегия, которая будет автоматизирована, заключается в следующем:

  1. Автоматический вход вучетную запись zerodha с помощью веб-драйвера Selenium.
  2. Проверкаинтервала максимум и минимумровно в 11 часов утра в режиме реального времени.
  3. Вход в сделку принарушении ценой интервала максимум или минимум.
  4. Нет входа в сделку, если ни интервальный максимум, ни интервальный минимум не нарушены.
  5. Если сделка введена, выход из сделки,когда цена нарушает заранее определенную цель или стоп-лосс.
  6. Выход из сделки в 15:00 независимо от позиции.

2. Файлы параметров и учетных данных

Для автоматического входа в систему учетные данные, такие как идентификатор пользователя, пароль, totp, ключ API и секрет API, должны быть переданы функциям. Лучше всего хранить эту информацию в формате json или yaml.

{
"api_key"        : "YOUR API KEY",
"api_secret"     : "YOUR API SECRET",
"user_id"        : "YOUR USER ID",
"password"       : "YOUR PASSWORD",
"totp"           : "YOUR TOTP",
"webdriver_path" : "./chromedriver",
"url"            : "https://kite.zerodha.com/"
}

Доступ и хранение этих параметров в переменных на python можно получить, используя следующий код

import json

with open(*your file path*,'r') as f:
    login = json.load(f)
    
username       = login['user_id']
password       = login['password']
totp           = login['totp']
api_key        = login['api_key']
api_secret     = login['api_secret']
webdriver_path = login['webdriver_path'] #path of your chromedriver
url            = login['url']

Аналогично торговые параметры могут храниться в переменных вформате yaml.

---
ticker_symbol: NSE:ONGC
trading_symbol: ONGC
quantity: 10
exit_hour: 14
exit_minute: 00
target_param: 5
sl_param: 7

ticker_symbol:символ инструмента, который необходимо передать в функции, чтобы получить интервал высокого минимума. например, ‘NSE:ONGC’ или ‘NFO:BANKNIFTYMAYFUT’.

trading_symbol: торговое название инструмента, который будет использоваться для выставления ордеров. например, ‘ONGC’ или ‘BANKNIFTYMAYFUT’.

количество: количество акций, подлежащих торговле.

exit_hour&exit_minute:час и минута, в которую сделка будет завершена, если она не вызовет условия выхода.

target_param: если сделка введена, целью будет интервальный максимум / минимум плюс / минус это target_param например, если интервальный максимум будет пробит, целью будет интервальный максимум + target_param а если минимум будет пробит, цель будет интервальным низким target_param.

sl_param: если интервальный максимум пробит, стоп-лосс будет интервальным максимумом -sl_param а если минимум пробит, то это будет интервальный минимум + target_param.

Чтение и хранение переменныхфайла yamlс помощью следующего кода

import yaml

from yaml.loader import SafeLoaderwith open('file.yaml') as f:
    trade = yaml.load(f, Loader=SafeLoader)
    
ticker_symbol  = trade['ticker_symbol']
trading_symbol = trade['trading_symbol']
quantity       = trade['quantity']
exit_hour      = trade['exit_hour']
exit_minute    = trade['exit_minute']
target_param   = trade['target_param']
sl_param       = trade['sl_param']

Заметка:

Имя файла json должно заканчиваться расширением .json, например, login_credentials.json, а имя файла yaml должно заканчиваться расширением .yaml, таким какlogin.yaml

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

3. Автоматизация процесса входа в систему

Процесс AlgoTrading начинается с автоматического входа в вашу учетную запись Zerodha и генерирует сеанс Кайта. Используя python и библиотекуkiteconnect, процесс входа в систему может быть легко автоматизирован при условии, что все учетные данные для входа хранятся в переменной.

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

from kiteconnect import KiteConnect

api_key = <YOUR API KEY>
api_secret = <YOUR API SECRET>

kite = KiteConnect(api_key = api_key)

после генерации кайт-сессии; запустить chrome с использованием селена и войти в учетную запись zerodha …

# launch chrome and open zerodha website

from selenium import webdriver
from time import sleep
from pyotp import TOTP

service = webdriver.chrome.service.Service(f'{webdriver_path}/chromedriver.exe')
service.start() 

options = webdriver.ChromeOptions()
options = options.to_capabilities()

driver  = webdriver.Remote(service.service_url, options)
driver.get(url)
driver.maximize_window()

Мы будем перенаправлены на страницу входа. Используя Selenium, учетные данные для входа могут быть автоматически вставлены…

#input username
user = driver.find_element_by_xpath("//input[@type = 'text']")
user.send_keys(username)

#input password
pwd = driver.find_element_by_xpath("//input[@type = 'password']")
pwd.send_keys(password)

#click on login
driver.find_element_by_xpath("//button[@type='submit']").click()

sleep(1)

#input totp
ztotp      = driver.find_element_by_xpath("//input[@type = 'text']")
totp_token = TOTP(totp)
token      = totp_token.now()
ztotp.send_keys(token)

#click on continue
driver.find_element_by_xpath("//button[@type = 'submit']").click()

После успешного входа в систему выполните следующий код, чтобы получить маркер доступа и установить соединение между python и kite API.

sleep(2)

#request token
request_token = driver.current_url.split('request_token=')[1].split('&')[0]

#access token
token = kite.generate_session(request_token, api_secret=api_secret)
access_token = token['access_token']

#connect python with Kite API
kite.set_access_token(access_token)

#closing chrome
driver.quit()

Теперь функции API кайта можно использовать для получения данных в реальном времени и выполнения ордеров на основе условий.

4. Кодирование стратегии

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

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

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

Их можно получить с помощью функцииkite.quote.

ticker_symbol = 'NSE:ONGC'

int_high = kite.quote(ticker_symbol)[ticker_symbol]['ohlc']['high']
int_low  = kite.quote(ticker_symbol)[ticker_symbol]['ohlc']['low']

print('int_high :' , int_high)
print('int_low :'  , int_low)

# output:
# >>> int_high : 170
# >>> int_low  : 165

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

4.2 Создать условие для входа в сделку

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

Перед созданием условий мы создаем функцию, которая является функцией ордера, используемой для размещения ордера. kite.place_order()функция используется для размещения ордеров.

######################### ORDER FUNCTION ##########################

def order(trading_symbol, t_type, quantity, kite):
    """
    This function will execute order. i.e place Buy/Sell order
    trading_symbol : symbol of the stock
    t_type         : type of order. 'BUY' or 'SELL'
    quantity       : quantity of the stocks to buy/sell
    kite           : kite object that is created earlier
    
    """
  
    kite.place_order(tradingsymbol = trading_symbol,
                          exchange = 'NSE',
                  transaction_type = t_type,
                          quantity = quantity,
                        order_type = 'MARKET',
                           variety = 'regular',
                           product = 'MIS')

Следующая функция —условие Entryбудет проверять ltp каждые 5 секунд и как только цена пробит максимум или минимум, функция ордера будет выполнена и ордер будет выставлен.

######################### ENTRY CONDITION ##########################
import datetime

for i in range(10000000):
    try:
        t = datetime.datetime.now()
        ltp = kite.ltp(ticker_symbol)[ticker_symbol]['last_price']
        print('ltp :', ltp)

        if ltp > int_high:
           order(trading_symbol, 'BUY', quantity, kite)
           print(f'High breached. TradeEntry : Long at Rs {ltp}')
           break

        elif ltp < int_low:
           order(trading_symbol, 'SELL', quantity, kite)
           print(f'Low breached. Trade Entry : Short at Rs.{ltp}')
           break

    except BaseException as error:
           print(f'{error} Exception occurred')

    #exit at 15:25 if neither int_high nor int_low breached
    if t.hour == 15 and t.minute == 25:
           print('Trade not entered today.')
           break

    sleep(5)

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

Мы используем методtry and except, чтобы игнорировать такие ошибки, какошибки подключенияилиошибки выполнения, которые могут возникнуть во время этого процесса и продолжаться без каких-либо перерывов.

4.3 Создание условий для выхода из сделки

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

Цель и стоп-лосс могут быть установлены на основе интервала максимум и минимум. В этом случае

### In case of High breach :target    = int_high + target_param = (170 + 5)
stop-loss = int_high - sl_param = (170 - 7)Target trigger : ltp ≥ target
stoploss trigger : ltp ≤ stoploss### Stock has to be sold in Exit condition

Послевыполнения условия exitфункция проверяет ltp каждые 5 секунд. Как только цель нарушена, функция ордера выполняется, акции продаются, а торговля выходит с прибылью. Если стоп-лосс нарушается, акции продаются, а торговля прекращается, но с убытком.

########################## EXIT CONDITION #########################

for i in range(1000000):    
    try:
        t = datetime.datetime.now()
        ltp = kite.ltp(ticker_symbol)[ticker_symbol]['last_price']
        print('ltp :', ltp)
   
        if (ltp >= target):
            order(trading_symbol, "SELL", quantity, kite)
            print(f'Target breached. Sold at Rs.{ltp}, PROFIT')
            break
            
        elif (ltp <= stop_loss):
            order(trading_symbol, "SELL", quantity, kite)
            print(f'stoploss breached. Sold at Rs.{ltp}, LOSS')
            break
            
        elif (t.hour == exit_hour) and (t.minute == exit_minute):
            order(trading_symbol, "SELL", quantity, kite)
            print(f'Trade Exit : No breach. Sold at Rs.{ltp}')
            break
            
    except BaseException as error:
        print(f'{error} Exception occurred.')
        
    sleep(5)

Одна из возможностей заключается в том, что ни цель, ни стоп-лосс не нарушаются. В этом случае мы выходим из сделки в определенное время выхода, которое предопределено. (exit_hour, exit_minute. 15:00 здесь).

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

### In case of low breach :target    = int_low - target_param = (165 - 5)
stop-loss = int_low + sl_param. = (165 + 7)Target trigger : ltp ≤ target
stop-loss trigger : ltp ≥ stoploss### stock has to be bought in Exit condition.

В этом случае функцией заказа будетпорядок (trading_symbol, «КУПИТЬ», количество, кайт)

Заметка:

1. В случаеторговли фьючерсами и опционами, ‘NSE’ должен быть заменен на ‘NFO‘ в параметре биржи в функции ордера.

2. Order_type может быть «рыночным» или «лимитным» в зависимости от требований.

3. Продукт является «MIS» для внутридневного. Это может быть «NRML» для ночного положения

4. Для получения дополнительной информации о функции place_order() ознакомьтесь с официальной документациейздесь.

5. Заключение

  • Мы увидели, как хранить учетные данные для входа и торговые параметры вформате jsonилиyaml.
  • Мы увидели, какавтоматизировать процесс входав учетную запись zerodha с помощью selenium webdriver.
  • Мы видели, какпример стратегии может быть закодирован на python.
  • Как получитьвысокие и низкие значения в режимереального времени с помощью API Kite.
  • Мы создалиусловия входаи ввели сделку автоматически.
  • Мы создалиусловия выходаи автоматически вышли из сделки.

Часть-2 Файлы логирования

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

СОДЕРЖАНИЕ

1. Введение в ведение журнала

2. Использование логирования в АлгоТрейдинге

3. Ведение журнала и уровни серьезности событий

4. Сохранение событий в лог-файле

5. Интеграция логирования с нашим предыдущим кодом

6. Заключение

7. Дальнейшие улучшения

1. Введение в ведение журнала

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

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

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

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

2. Использование логирования в АлгоТрейдинге

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

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

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

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

3. Ведение журнала и уровни серьезности событий

Уровни серьезности и эквивалентные им числовые значения

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

  1. ОТЛАДКА : Он используется для предоставления подробной информации и используется только при наличии проблем с диагностикой.
  2. ИНФОРМАЦИЯ : Он предоставляет информацию о том, что все работает так, как мы хотим.
  3. ПРЕДУПРЕЖДЕНИЕ Он используется для того, чтобы предупредить о том, что что-то случилось неожиданно, или мы столкнемся с проблемой в ближайшее время.
  4. ОШИБКА Он используется для информирования о том, что у нас есть какие-то серьезные проблемы, программное обеспечение не выполнило некоторые программы.
  5. КРИТИЧЕСКИЙ : Он указывает на серьезную ошибку, сама программа может быть не в состоянии продолжать выполнение.

Эти уровни можно использовать в соответствии с нашими потребностями и регистрировать события. Давайте посмотрим на пример…

https://medium.com/media/f56f8698b8c63f22c97934a954f2d451

Обратите внимание, что сообщения debug() и info() не отображали сообщения, потому что по умолчанию модуль журнала регистрирует сообщения с уровнем серьезности WARNING, ERROR и CRITICAL. Хотя мы можем установить уровень серьезности и регистрировать события по своему усмотрению.

4. Сохранение событий в лог-файле

В приведенном выше примере вывод выводится только в консоли. Если мы хотим сохранить их в файле, мы должны использовать функцию basicConfic(). Модуль ведения журнала предоставляет basicConfig(**kwarg), используемый для настройки ведения журнала.

https://medium.com/media/793375e72452ab4ea7edc2a7f36e97f6

basicConfig принимает четыре основных аргумента…

  • Имя файла В нем указывается имя файла.
  • Режим файла Он открывает файл в определенном режиме. По умолчанию для открывающегося файла используется режим ‘a’, что означает, что мы можем добавить содержимое. Буква «w» означает написание.
  • Формат Формат определяет формат сообщения журнала.
  • Уровень Указанный уровень серьезности задается корневым уровнем.

Файл будет сохранен с именем test.log, а содержимое будет выглядеть следующим образом…

Мы можем установить уровень серьезности в basicConfig(), передав аргумент level. Установив для важности значение INFO, каждое сообщение будет входить в файл.

https://medium.com/media/66c6d7c9bb3a9abbce13b8f522abd412

5. Интеграция логирования с нашим предыдущим кодом

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

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

https://medium.com/media/41cd8a9d4c033a4b801320b620ff4f62

Файл будет сохранен с именем; 13_5_2022_BANKNIFTYMAYFUT.log

  1. Допустим, мы хотим записывать в файл максимум, минимум, ltp интервала каждые несколько секунд. Как только условие входа будет выполнено, будет зарегистрировано в файле, что сделка открыта и по какой цене.

https://medium.com/media/e6225ccd958c5ecb9e8d6d2c96546c52

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

2. Теперь, допустим, после входа в сделку мы хотим увидеть, когда было выполнено условие выхода и каковы были цель и стоп-лосс. Код будет таким…

https://medium.com/media/f74571486a26e7d5591d236498a97805

Если условие выхода выполнено, лог-файл будет выглядеть следующим образом…

6. Заключение

  • Мы увидели, что такое модуль Logging в python.
  • Чем может быть полезна запись серии событий в AlgoTrading.
  • Уровни серьезности (отладка, информация, предупреждение, ошибка и критический).
  • Как создать лог-файл, изменить уровень критичности и определить его формат.
  • И, наконец, как интегрироваться с нашим кодом, чтобы регистрировать каждое событие, происходящее во время процесса.

7. Дальнейшие улучшения

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

Поэтому в нашей следующей статье мы поговорим о том, как получать обновления (сообщения) в режиме реального времени в вашем приложении Telegram. Мы создадим Telegram-бота, который будет сообщать нам о том, когда сделка была заключена и закрыта в режиме реального времени.

Часть-3 Обновления Telegram-бота

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

СОДЕРЖАНИЕ

1. Внедрение и использование ботов в алготрейдинге

2. Создайте Telegram-бота и активируйте его

3. Создайте идентификатор чата

4. Отправить сообщение через бота

5. Интеграция бота с нашей стратегией

6. Заключение

1. Внедрение и использование ботов в алготрейдинге

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

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

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

2. Создайте Telegram-бота и активируйте его

Для создания телеграм-бота выполните следующие действия:

1. Войдите в приложение Telegram на своем устройстве или в Telegram Web для ПК.

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

3. Откроется окно чата… Вам нужно нажать на кнопку «Пуск»

4. Вы автоматически получите ответ о том, что вам нужно сделать, как показано на рисунке…

5. Нажмите на /newbot или введите /newbot в чате и нажмите Enter.

6. Теперь вам нужно ввести имя вашего бота.

7. Теперь введите имя пользователя вашего бота. Он должен быть уникальным и заканчиваться на ‘bot’.

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

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

Примечание: Если у вас уже есть бот и вы не знаете токен доступа, просто перейдите в @BotFather > /start > /mybots > API Token

3. Создайте идентификатор чата

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

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

1. Перейдите по следующей ссылке в браузере… Не забудьте передать маркер доступа бота

https://api.telegram.org/bot<токен доступа бота>/getUpdates

Если все прошло нормально, то в результате получится что-то вроде этого

2. Теперь отправьте боту любое сообщение типа «Привет» и снова обновите ссылку. У вас должно получиться что-то вроде этого…

Теперь вы можете увидеть свой идентификатор чата. Это 10-значное число. Сохраните его, так как нам нужно использовать его в нашем коде вместе с токеном доступа.

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

4. Отправить сообщение через бота

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

https://medium.com/media/a17d9ec4a486d5a0209b0ccce1e7e748

При выполнении приведенного выше кода мы получим сообщение

5. Интеграция телеграм-бота с нашим кодом

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

https://medium.com/media/5ac24563624522a5898b1ff53a5e2b1e

Мы добавим эту функцию к нашим условиям входа и выхода в коде, чтобы получать сообщения при срабатывании условия

https://medium.com/media/4bf041e4a638654fd15fa043bb746300

Точно так же мы можем добавить эту функцию в наше условие выхода

https://medium.com/media/fec3d85619d96fbf01123044e97be028

Допустим, сработали условия входа и выхода, мы получим такие обновления…

Вот так мы можем просмотреть события торговли с помощью бота в телеграме.

6. Заключение

  • Мы увидели использование ботов в AlgoTrading.
  • Мы создали бота в телеграме и активировали его.
  • Мы рассмотрели, как получить токен доступа для нового бота (или существующего бота) и идентификатор чата.
  • Мы увидели, как отправлять сообщения через бота с помощью модуля requests.
  • Наконец, мы интегрировали бота с нашим кодом, чтобы получать своевременные обновления событий.

Тонкая настройка алгоритмической стратегии с помощью оптимизации роя частиц

Как определить неоптимальные параметры торговой стратегии, если количество комбинаций крайне велико.

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

Содержание

  1. Ненавязчивое введение в оптимизацию роя частиц
  2. Алгоритм торговой стратегии
  3. Давайте напишем код!
  4. Эксперимент
  5. Дальнейшие улучшения
  6. Сводка

Ненавязчивое введение в оптимизацию роя частиц

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

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

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

Из Википедии:

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

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

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

Схема алгоритма PSO (из статьи)
Анимация PSO (из Википедии)

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

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

Псевдокод алгоритма (из статьи)

Хорошее объяснение алгоритма с практической реализацией 

Тарлан Ахадли Вы можете найти здесь. Кроме того, вы можете посмотреть это замечательное видео

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

Алгоритм торговой стратегии

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

Линия тренда основана на двух точках разворота. Для восходящей линии тренда (зеленая нижняя линия) следующая нисходящая точка разворота выше предыдущей, а следующая восходящая точка разворота ниже предыдущей для нисходящего тренда (красная верхняя линия).

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

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

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

Давайте напишем код!

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

Этот проект я разделил на 5 файлов, где 4 файла состоят из класса, а один файл представляет собой скрипт, который содержит основной эксперимент и запускает остальные.

Код main.py содержит здесь логику эксперимента.

https://towardsdatascience.com/media/9fbccb314831ca9fefdf3dc113648cf7

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

В этом скрипте мы создадим объект класса BacktestTrendBreakerPL. Этот класс отвечает за чтение данных, запуск стратегии, вычисление метрик и результатов в визуализации. Код BacktestTrendBreakerPL.py класса

https://towardsdatascience.com/media/8ff40a7552eb20e8a9e2a4273796752e

За детализацию отвечает output_settings объект, который мы передали из основного скрипта.

Самое интересное – это stability_of_timeseries функция. Эта функция является сердцем исследований, которые напрямую влияют на процесс оптимизации. Основной целью является оценка качества торговой стратегии с пройденным набором параметров. Математически он определяется как R-квадрат линейной аппроксимации к кумулятивным логарифмическим доходам. Знак функции в зависимости от доходности. Когда значение близко к 1.0, это означает отличный результат и линия растет вверх с небольшими просадками, плохой результат, когда это значение близко к -1.0.

Этот скрипт содержит класс FinamHLOC из DataFeedFormat.py, где размещен код для структуры данных исходного CSV-файла.

https://towardsdatascience.com/media/490b608477f9d09b646daed778465459

Следующая интересная вещь — это объект класса TrendBreakerPL из TrendBreakerPLStrategy.py.

https://towardsdatascience.com/media/d1cbb0f9bb8bda8d5c75744ade12cca4

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

Дополнительным функционалом является печать статуса заказа.

Этот скрипт использует сигналы, основанные на данных индикатора, которые были определены в классе PivotPointLine в PivotPointLineIndicator.py. Базовым функционалом является расчет точек разворота, линий тренда и сигнала при пересечении ценой закрытия и закрытие верхней\нижней линии тренда в зависимости от направления.

https://towardsdatascience.com/media/247df80f2e9cd09555ec365c93eda344

Я считаю, что этот код не оптимален, он занимает много времени в общем пайплайне бэктестинга.

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

Кроме того, я изменил несколько строк кода в plotting.py Pyfolio следующим образом, потому что мне нужен объект DataFrame

https://towardsdatascience.com/media/9ecb4f7ba8f0c074a8ca231f200423e6

Эксперимент

Эксперимент проводится на данных Сбербанка. Сбербанк является крупнейшим банком в России, Восточной и Центральной Европе.

Я скачал почасовые данные от Финама. Период начинается с 6 января 2014 года по 21 февраля 2020 года. Затем этот период был разделен на

период движения поездов (6 января 2014 г. — 29 декабря 2017 г.);

тестовый период (3 января 2018 г. — 21 февраля 2020 г.).

В набор параметров оптимизации входят:pivot_window_len — window length (the number of bars) by each side for determing the pivot point. Range is [2, 120];history_bars_as_multiple_pwl — history window length as a multiplication factor for pivot_window_len. Range is [10, 100];fixed_tp — value for the fixed level of take profit. Range is [0.01, 0.2];fixed_sl_as_multiple_tp — value for the fixed level of stop loss as a multiplication factor for fixed_tp. Range is [0.1, 1.5].I used a multiplication factor instead of a simple value because it is easier to control and logically suitable in an optimization process. E.g., take profit and stop loss will be closer to each other.

Эксперимент был запущен в 40 итераций с размером роя 20. Остальные параметры роевой оптимизации по умолчанию (omega=0.5, phip=0.5, phig=0.5). Наилучший набор параметров для данных поезда:pivot_window_len = 9history_bars_as_multiple_pwl = 49fixed_tp = 0.06034064 (6.034%)fixed_sl_as_multiple_tp = 0.14778282. It means stop loss is 0.892%As a result the objective function is -0.9712, then stability is 0.9712. This is very good result because it’s very close to 1.0.

Давайте посмотрим на кривую капитала нашего эксперимента

Выглядит довольно неплохо. На периоде поезда мы имеем тот же уровень доходности с меньшей просадкой и более стабильной кривой. Кривая алгоритма на тестовом периоде демонстрирует сильную положительную динамику, значительно лучше, чем бенчмарк (buy & hold). Конечная кривая сильно лучше бенчмарка. Метрики алгоритма:Train:
Return: 106.45%
Stability: 0.971
Maximum Drawdown: 14.37%Test:
Return: 63.17%
Stability: 0.7934
Maximum Drawdown: 11.58%Full Period:
Return: 296.3%
Stability: 0.9738
Maximum Drawdown: 14.37%

На следующем рисунке более эффективно показаны периоды просадки

Хорошим моментом является то, что уровень просадки стабилен в течение тренировочного и тестового периодов. Даже на тестовом периоде ниже. Давайте посмотрим на Топ-5 просадок

Train:
Net drawdown in % Peak date Valley date Recovery date Duration
1 14.365 2017-06-15 2017-12-27 NaT NaN
2 13.2355 2015-05-20 2015-07-07 2015-11-17 130
3 12.8431 2014-10-06 2014-12-02 2014-12-16 51
4 10.1103 2015-01-27 2015-02-12 2015-03-16 35
5 8.6618 2014-04-01 2014-04-22 2014-06-16 55Test:
Net drawdown in % Peak date Valley date Recovery date Duration
1 11.5807 2019-02-08 2019-03-18 2019-04-23 52
2 10.8415 2019-07-23 2019-12-02 NaT NaN
3 9.28844 2018-05-03 2018-06-25 2018-07-27 62
4 8.26704 2018-10-30 2018-12-03 2019-01-18 59
5 7.07621 2018-04-03 2018-04-09 2018-04-09 5Full Period:
Net drawdown in % Peak date Valley date Recovery date Duration
1 14.365 2017-06-15 2017-12-27 2018-03-21 200
2 13.2355 2015-05-20 2015-07-07 2015-11-17 130
3 12.8431 2014-10-06 2014-12-02 2014-12-16 51
4 12.1112 2019-02-08 2019-03-18 2019-05-03 60
5 11.5791 2019-07-23 2019-12-02 NaT NaN

Как мы видим, наибольшая просадка была на железнодорожном периоде.

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

Давайте посмотрим на квантильный график доходности для разных таймфреймов

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

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

Дальнейшие улучшения

Я бы рекомендовал изучить следующие моменты, чтобы получить лучшие результаты:

  1. Поэкспериментируйте с параметрами PSO. Также можно попробовать использовать другой подход для оптимизации вместо PSO, тогда вы сможете сравнивать друг друга.
  2. Тейк-профит и стоп-лосс на основе волатильности.
  3. Трейлинг-стоп вместо тейк-профита и стоп-лосса.
  4. Ограниченное время нахождения в должности.
  5. Срабатывание любого паттерна для закрытия позиции (например, пересечение трендовой линии во время нахождения в позиции).
  6. Различные таймфреймы (например, 15 минут или 1 день).
  7. Примените пошаговый форвард-анализ для исследования стабильности и надежности результата.
Walk-Forwarding Process (из статьи)

8. Создайте портфель на основе нескольких некоррелированных активов.

9. Добавить механизм стресс-тестирования для проверки надежности стратегии.

10. Добавьте дополнительные метрики для кумулятивного периода Walk-Forward и кривой портфеля, такие как альфа, бетакоэффициент Сортино и т. д.

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

  1. Сделайте рефакторинг PivotPointLineIndicator.py, так как это очень медленная реализация, или подготовьте заранее рассчитанный кэш индикатора для конкретных таймфреймов и параметров.
  2. Старайтесь использовать GPU там, где это возможно.
  3. Используйте многопроцессорный режим при формировании портфеля.
  4. Ознакомьтесь с другими реализациями подхода PSO. Некоторые библиотеки могут выполнять вычисления в многопроцессорном режиме GPU и CPU.

Сводка

  1. Описано краткое введение в подход оптимизации роя частиц и его применение в алгоритмической торговле.
  2. Формализовал закономерности и логику торговой стратегии.
  3. Продемонстрировал код с описанием, которое можно получить на GitHub. Кроме того, обратите внимание на packages_env_list.txt для установки правильных версий пакетов.
  4. Обучил и протестировал подход, а также оценил эксперимент.
  5. Предложены дальнейшие улучшения.

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

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

Антуан Вулкан 

Алгоритмическая торговля со стохастическим осциллятором

Существует множество технических индикаторов, которые можно рассматривать для исследования и анализа, но тот, который мы собираемся обсудить сегодня, является одним из самых популярных индикаторов, используемых среди трейдеров в торговых целях. Это не что иное, как технический индикатор стохастического осциллятора. В этой статье мы будем использовать python для создания торговой стратегии на основе стохастического осциллятора и протестируем стратегию, чтобы увидеть, насколько хорошо она работает на реальном рынке. Кроме того, мы также сравним наши торговые результаты с SPY ETF (ETF, специально разработанным для отслеживания рыночного индекса S&P 500) в качестве метода проверки нашей стратегии. Без лишних слов, давайте перейдем к статье.

Прежде чем двигаться дальше, если вы хотите протестировать свои торговые стратегии без какого-либо кодирования, для этого есть решение. Это BacktestZone. Это платформа для бесплатного тестирования любого количества торговых стратегий на различных типах торгуемых активов без кодирования. Вы можете использовать инструмент прямо сейчас, используя ссылку здесь: https://www.backtestzone.com/

Стохастический осциллятор

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

Значения стохастического осциллятора всегда лежат в диапазоне от 0 до 100 из-за его функции нормализации. Общие уровни перекупленности и перепроданности считаются 80 и 20 соответственно, но они могут варьироваться от одного человека к другому. Стохастический осциллятор состоит из двух основных компонентов:

  • Линия %K: Эта линия является наиболее важным и основным компонентом индикатора стохастического осциллятора. Он также известен как индикатор быстрого стохастика. Единственная цель этой линии — выразить текущее состояние рынка (перекупленность или перепроданность). Эта линия рассчитывается путем вычитания самой низкой цены, достигнутой акциями за указанное количество периодов, из цены закрытия акции, а затем эта разница делится на значение, рассчитанное путем вычитания самой низкой цены, достигнутой акцией за указанное количество периодов, из самой высокой цены акции. Окончательное значение получается путем умножения значения, рассчитанного на основе вышеупомянутых шагов, на 100. Способ вычисления линии %K с наиболее популярной установкой 14 в качестве количества периодов можно представить следующим образом:
%K = 100 * ((14 DAY CLOSING PRICE - 14 DAY LOWEST PRICE) - (14 DAY HIGHEST PRICE - 14 DAY LOWEST PRICE))
  • Линия %D: Также известный как медленный стохастический индикатор, представляет собой не что иное, как скользящую среднюю линии %K за определенный период. Он также известен как гладкая версия линии %K, так как линейный график линии %D будет выглядеть более плавным, чем линия %K. Стандартная настройка линии %D равна 3 в качестве количества периодов.

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

Торговая стратегия стохастического осциллятора: Наша торговая стратегия покажет сигнал на покупку, когда:

  • Линия %К ниже 20
  • Линия %D ниже 20
  • Линия %K находится ниже линии %D

Точно так же наша стратегия покажет сигнал на продажу, когда:

  • Линия %К выше 80
  • Линия %D выше 80
  • Линия %K находится над линией %D

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

IF %K LINE < 20 AND %D LINE < 20 AND %K LINE < %D LINE => BUY
IF %K LINE > 80 AND %D LINE > 80 AND %K LINE > %D LINE => SELL

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

Реализация на Python

Часть кодирования подразделяется на различные этапы следующим образом:

1. Importing Packages
2. Extracting Data from Alpha Vantage
3. Extracting the Stochastic Oscillator values
4. Stochastic Oscillator Plot
5. Creating the Trading Strategy
6. Plotting the Trading Lists
7. Creating our Position
8. Backtesting
9. SPY ETF Comparison

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

Шаг 1: Импорт пакетов

Импорт необходимых пакетов в среду python — это шаг, который нельзя пропустить. Первичными пакетами будут Pandas для работы с данными, NumPy для работы с массивами и сложными функциями, Matplotlib для построения графиков и Requests для выполнения вызовов API. Вторичными пакетами будут Math для математических функций и Termcolor для настройки шрифта (необязательно).

Реализация Python:

import pandas as pd
import numpy as np
import requests
from termcolor import colored as cl
from math import floor
import matplotlib.pyplot as pltplt.rcParams[‘figure.figsize’] = (20, 10)
plt.style.use(‘fivethirtyeight’)

Теперь, когда мы импортировали все необходимые пакеты в нашу среду python. Давайте продолжим извлекать исторические данные Netflix с помощью мощного стандартного API Alpha Vantage.

Шаг 2: Извлечение данных из Alpha Vantage

На этом этапе мы собираемся извлечь исторические данные Netflix с помощью конечной точки API, предоставленной Alpha Vantage. Перед этим примечание об Alpha Vantage: Alpha Vantage предоставляет бесплатные биржевые API, с помощью которых пользователи могут получить доступ к широкому спектру данных, таких как обновления в реальном времени и исторические данные об акциях, валютах и криптовалютах. Убедитесь, что у вас есть учетная запись на Alpha Vantage, только тогда вы сможете получить доступ к своему секретному ключу API (важный элемент для извлечения данных с помощью API).

Реализация Python:

def get_historical_data(symbol, start_date = None):
api_key = open(r'api_key.txt')
api_url = f'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol={symbol}&apikey={api_key}&outputsize=full'
raw_df = requests.get(api_url).json()
df = pd.DataFrame(raw_df[f'Time Series (Daily)']).T
df = df.rename(columns = {'1. open': 'open', '2. high': 'high', '3. low': 'low', '4. close': 'Close', '5. adjusted close': 'adj close', '6. volume': 'volume'})
for i in df.columns:
df[i] = df[i].astype(float)
df.index = pd.to_datetime(df.index)
df = df.iloc[::-1].drop(['7. dividend amount', '8. split coefficient'], axis = 1)
if start_date:
df = df[df.index >= start_date]
return df

nflx = get_historical_data('NFLX', '2020-01-01')
nflx

Выпуск:

Изображение автора

Пояснение к коду: Первое, что мы сделали, это определили функцию с именем ‘get_historical_data’, которая принимает символ акции (‘symbol’) в качестве обязательного параметра и дату начала исторических данных (‘start_date’) в качестве необязательного параметра. Внутри функции мы определяем ключ API и URL-адрес и сохраняем их в соответствующей переменной. Далее мы извлекаем исторические данные в формате JSON с помощью функции «get» и сохраняем их в переменной «raw_df». После выполнения некоторых процессов по очистке и форматированию необработанных данных JSON мы возвращаем их в виде чистого кадра данных Pandas. Наконец, мы вызываем созданную функцию, чтобы извлечь исторические данные Netflix с начала 2020 года и сохранить их в переменной nflx.

Шаг 3: Извлечение значений стохастического осциллятора

На этом этапе мы собираемся извлечь значения стохастического осциллятора Netflix с помощью конечной точки API, предоставляемой Alpha Vantage. Этот шаг почти аналогичен тому, что мы делали на предыдущем шаге.

Реализация Python:

def get_stoch(symbol, k_period, d_period, start_date):
api_key = open(r'api_key.txt')
url = f'https://www.alphavantage.co/query?function=STOCH&symbol={symbol}&interval=daily&fastkperiod={k_period}&slowdperiod={d_period}&apikey={api_key}'
raw = requests.get(url).json()
df = pd.DataFrame(raw['Technical Analysis: STOCH']).T.iloc[::-1]
df = df[df.index >= start_date]
df.index = pd.to_datetime(df.index)
df = df.astype(float)
return df['SlowK'], df['SlowD']

nflx['%k'], nflx['%d'] = get_stoch('NFLX', 14, 3, '2020-01-01')
nflx = nflx.dropna()
nflx.head()

Выпуск:

Изображение автора

Пояснение к коду: Во-первых, мы определяем функцию с именем ‘get_stoch’, которая принимает в качестве параметров символ акции (‘symbol’), количество периодов для линии %K (‘k_period’), количество периодов для линии %D (‘d_period’) и дату начала данных (‘start_date’). Внутри функции мы сначала назначаем две переменные с именами «api_key» и «url» для хранения ключа API и URL-адреса API соответственно. Используя функцию ‘get’, предоставляемую пакетом Requests, мы вызываем API и сохраняем ответ в переменной ‘raw’. После выполнения некоторых манипуляций с данными мы возвращаем значения %K и %D. Наконец, мы вызываем функцию для извлечения значений стохастического осциллятора Netflix.

Шаг 4: График стохастического осциллятора

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

Реализация Python:

def plot_stoch(symbol, price, k, d):
ax1 = plt.subplot2grid((9, 1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((9, 1), (6,0), rowspan = 3, colspan = 1)
ax1.plot(price)
ax1.set_title(f'{symbol} STOCK PRICE')
ax2.plot(k, color = 'deepskyblue', linewidth = 1.5, label = '%K')
ax2.plot(d, color = 'orange', linewidth = 1.5, label = '%D')
ax2.axhline(80, color = 'black', linewidth = 1, linestyle = '--')
ax2.axhline(20, color = 'black', linewidth = 1, linestyle = '--')
ax2.set_title(f'{symbol} STOCH')
ax2.legend()
plt.show()

plot_stoch('NFLX', nflx['Close'], nflx['%k'], nflx['%d'])

Выпуск:

Изображение автора

Участок подразделяется на две панели: верхнюю и нижнюю. Верхняя панель представляет собой линейный график цены закрытия Netflix. Нижняя панель содержит компоненты стохастического осциллятора. Будучи опережающим индикатором, стохастический осциллятор не может быть построен рядом с ценой закрытия, так как значения индикатора и цены закрытия сильно различаются. Таким образом, он строится отдельно от цены закрытия (ниже цены закрытия в нашем случае). Составляющие линии %K и %D, которые мы обсуждали ранее, обозначены синим и оранжевым цветом соответственно. Вы также можете заметить две дополнительные черные пунктирные линии выше и ниже линии %K и %D. Это дополнительный компонент стохастического осциллятора, известный как полосы. Эти полосы используются, чтобы выделить область перекупленности и перепроданности. Если линии %K и %D пересекаются выше верхней полосы, то акция считается перекупленной. Аналогичным образом, когда линии %K и %D пересекаются ниже нижней полосы, акция считается перепроданной.

Шаг 5: Создание торговой стратегии

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

Реализация Python:

def implement_stoch_strategy(prices, k, d):    
buy_price = []
sell_price = []
stoch_signal = []
signal = 0

for i in range(len(prices)):
if k[i] < 20 and d[i] < 20 and k[i] < d[i]:
if signal != 1:
buy_price.append(prices[i])
sell_price.append(np.nan)
signal = 1
stoch_signal.append(signal)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
stoch_signal.append(0)
elif k[i] > 80 and d[i] > 80 and k[i] > d[i]:
if signal != -1:
buy_price.append(np.nan)
sell_price.append(prices[i])
signal = -1
stoch_signal.append(signal)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
stoch_signal.append(0)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
stoch_signal.append(0)

return buy_price, sell_price, stoch_signal

buy_price, sell_price, stoch_signal = implement_stoch_strategy(nflx['Close'], nflx['%k'], nflx['%d'])

Пояснение к коду: Во-первых, мы определяем функцию с именем ‘implement_stoch_strategy’, которая принимает цены акций (‘price), а в качестве параметров значения линий %K (‘k’) и %D (‘d’).

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

После этого мы реализуем торговую стратегию через фор-цикл. Внутри for-цикла мы передаем определенные условия, и если условия выполняются, соответствующие значения будут добавлены в пустые списки. Если условие для покупки акций будет выполнено, цена покупки будет добавлена в список «buy_price», а значение сигнала будет добавлено как 1, представляющее покупку акций. Точно так же, если условие продажи акций будет выполнено, цена продажи будет добавлена к списку «sell_price», а значение сигнала будет добавлено как -1, представляющее продажу акции.

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

Шаг 6: Построение торговых сигналов

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

Реализация Python:

ax1 = plt.subplot2grid((9, 1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((9, 1), (6,0), rowspan = 3, colspan = 1)
ax1.plot(nflx['Close'], color = 'skyblue', label = 'NFLX')
ax1.plot(nflx.index, buy_price, marker = '^', color = 'green', markersize = 10, label = 'BUY SIGNAL', linewidth = 0)
ax1.plot(nflx.index, sell_price, marker = 'v', color = 'r', markersize = 10, label = 'SELL SIGNAL', linewidth = 0)
ax1.legend(loc = 'upper left')
ax1.set_title('NFLX STOCK PRICE')
ax2.plot(nflx['%k'], color = 'deepskyblue', linewidth = 1.5, label = '%K')
ax2.plot(nflx['%d'], color = 'orange', linewidth = 1.5, label = '%D')
ax2.axhline(80, color = 'black', linewidth = 1, linestyle = '--')
ax2.axhline(20, color = 'black', linewidth = 1, linestyle = '--')
ax2.set_title('NFLX STOCH')
ax2.legend()
plt.show()

Выпуск:

Изображение автора

Пояснение к коду: Мы строим график компонентов стохастического осциллятора вместе с сигналами покупки и продажи, генерируемыми торговой стратегией. Мы можем наблюдать, что всякий раз, когда линии %K и %D пересекаются ниже нижней полосы, а линия %K пересекается ниже линии %D, на графике отображается зеленый сигнал на покупку. Аналогичным образом, всякий раз, когда линии %K и %D пересекаются выше верхней полосы, а линия %K пересекается выше линии %D, на графике отображается сигнал на продажу, окрашенный в красный цвет.

Шаг 7: Создание нашей позиции

На этом этапе мы собираемся создать список, в котором будет указано 1, если мы владеем акциями, или 0, если мы не владеем акциями или не держим их.

Реализация Python:

position = []
for i in range(len(stoch_signal)):
if stoch_signal[i] > 1:
position.append(0)
else:
position.append(1)

for i in range(len(nflx['Close'])):
if stoch_signal[i] == 1:
position[i] = 1
elif stoch_signal[i] == -1:
position[i] = 0
else:
position[i] = position[i-1]

k = nflx['%k']
d = nflx['%d']
close_price = nflx['Close']
stoch_signal = pd.DataFrame(stoch_signal).rename(columns = {0:'stoch_signal'}).set_index(nflx.index)
position = pd.DataFrame(position).rename(columns = {0:'stoch_position'}).set_index(nflx.index)

frames = [close_price, k, d, stoch_signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)

strategy.tail()

Выпуск:

Изображение автора

Пояснение к коду: Во-первых, мы создаем пустой список с именем «позиция». Мы проходим два цикла for, один из которых предназначен для генерации значений для списка «позиция», чтобы они точно соответствовали длине списка «сигналов». Другой цикл for-loop — это тот, который мы используем для генерации фактических значений позиции. Внутри второго цикла for, мы перебираем значения списка ‘signal’, и добавляются значения списка ‘position’ в зависимости от того, какое условие выполняется. Стоимость позиции остается 1, если мы владеем акциями, или остается 0, если мы продали или не владеем акциями. Наконец, мы делаем некоторые манипуляции с данными, чтобы объединить все созданные списки в один фрейм данных.

Из показанного вывода мы видим, что в первой строке наша позиция по акциям осталась равной 0 (поскольку сигнал стохастического осциллятора не изменился), но наша позиция внезапно превратилась в 1, когда мы купили акцию, когда торговый сигнал стохастического осциллятора представляет собой сигнал на покупку (1). Наша позиция будет оставаться 1 до тех пор, пока не произойдут некоторые изменения в торговом сигнале. Теперь пришло время внедрить процесс тестирования на истории!

Шаг 8: Тестирование на истории

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

Реализация Python:

nflx_ret = pd.DataFrame(np.diff(nflx['Close'])).rename(columns = {0:'returns'})
stoch_strategy_ret = []

for i in range(len(nflx_ret)):
try:
returns = nflx_ret['returns'][i]*strategy['stoch_position'][i]
stoch_strategy_ret.append(returns)
except:
pass

stoch_strategy_ret_df = pd.DataFrame(stoch_strategy_ret).rename(columns = {0:'stoch_returns'})

investment_value = 100000
number_of_stocks = floor(investment_value/nflx['Close'][-1])
stoch_investment_ret = []

for i in range(len(stoch_strategy_ret_df['stoch_returns'])):
returns = number_of_stocks*stoch_strategy_ret_df['stoch_returns'][i]
stoch_investment_ret.append(returns)

stoch_investment_ret_df = pd.DataFrame(stoch_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(stoch_investment_ret_df['investment_returns']), 2)
profit_percentage = floor((total_investment_ret/investment_value)*100)
print(cl('Profit gained from the STOCH strategy by investing $100k in NFLX : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the STOCH strategy : {}%'.format(profit_percentage), attrs = ['bold']))

Выпуск:

Profit gained from the STOCH strategy by investing $100k in NFLX : 45001.44
Profit percentage of the STOCH strategy : 45%

Пояснение к коду: Во-первых, мы рассчитываем доходность акций Netflix с помощью функции «diff», предоставляемой пакетом NumPy, и сохраняем ее в виде кадра данных в переменной «nflx_ret». Затем мы передаем цикл for, чтобы перебрать значения переменной «nflx_ret» для расчета доходности, которую мы получили от нашей торговой стратегии стохастического осциллятора, и эти значения доходности добавляются к списку «stoch_strategy_ret». Затем мы преобразуем список «stoch_strategy_ret» в кадр данных и сохраняем его в переменной «stoch_strategy_ret_df».

Далее идет процесс тестирования на истории. Мы собираемся протестировать нашу стратегию, вложив сто тысяч долларов США в нашу торговую стратегию. Итак, во-первых, мы храним сумму инвестиций в переменной «investment_value». После этого мы рассчитываем количество акций Netflix, которые мы можем купить, используя сумму инвестиций. Вы можете заметить, что я использовал функцию «пол», предоставляемую математическим пакетом, потому что, деля сумму инвестиций на цену закрытия акций Netflix, он выдает вывод с десятичными числами. Количество акций должно быть целым, а не десятичным числом. Используя функцию «пол», мы можем вырезать десятичные дроби. Помните, что функция «пол» намного сложнее, чем функция «круглый». Затем мы проходим цикл for, чтобы найти доходность инвестиций, за которым следуют некоторые задачи по манипулированию данными.

Наконец, мы печатаем общий доход, который мы получили, инвестировав сто тысяч в нашу торговую стратегию, и выясняется, что мы получили приблизительную прибыль в размере сорока тысяч долларов США за один год. Это неплохо! Теперь давайте сравним нашу доходность с доходностью SPY ETF (ETF, предназначенный для отслеживания индекса фондового рынка S&P 500).

Шаг 9: Сравнение SPY ETF

Этот шаг не является обязательным, но настоятельно рекомендуется, так как мы можем получить представление о том, насколько хорошо наша торговая стратегия работает по сравнению с эталоном (SPY ETF). На этом этапе мы собираемся извлечь данные SPY ETF с помощью функции «get_historical_data», которую мы создали, и сравнить доходность, которую мы получаем от SPY ETF, с доходностью нашей стратегии стохастического осциллятора на Netflix.

Реализация Python:

def get_benchmark(start_date, investment_value):
spy = get_historical_data('SPY', start_date)['Close']
benchmark = pd.DataFrame(np.diff(spy)).rename(columns = {0:'benchmark_returns'})

investment_value = investment_value
number_of_stocks = floor(investment_value/spy[-1])
benchmark_investment_ret = []

for i in range(len(benchmark['benchmark_returns'])):
returns = number_of_stocks*benchmark['benchmark_returns'][i]
benchmark_investment_ret.append(returns)

benchmark_investment_ret_df = pd.DataFrame(benchmark_investment_ret).rename(columns = {0:'investment_returns'})
return benchmark_investment_ret_df

benchmark = get_benchmark('2020-01-01', 100000)
investment_value = 100000
total_benchmark_investment_ret = round(sum(benchmark['investment_returns']), 2)
benchmark_profit_percentage = floor((total_benchmark_investment_ret/investment_value)*100)
print(cl('Benchmark profit by investing $100k : {}'.format(total_benchmark_investment_ret), attrs = ['bold']))
print(cl('Benchmark Profit percentage : {}%'.format(benchmark_profit_percentage), attrs = ['bold']))
print(cl('STOCH Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage), attrs = ['bold']))

Выпуск:

Benchmark profit by investing $100k : 21780.0
Benchmark Profit percentage : 21%
STOCH Strategy profit is 24% higher than the Benchmark Profit

Пояснение к коду: Код, используемый на этом этапе, почти аналогичен тому, который использовался на предыдущем этапе тестирования на истории, но вместо того, чтобы инвестировать в Netflix, мы инвестируем в SPY ETF, не реализуя никаких торговых стратегий. Из вывода мы видим, что наша торговая стратегия стохастического осциллятора превзошла SPY ETF на 24%. Это здорово!

Заключение

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

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

Ну вот! Если вы забыли следовать какой-либо из частей кодирования, не волнуйтесь. Я предоставил полный исходный код в конце этой статьи. Надеюсь, вы узнали что-то полезное из этой статьи.

Полный код:

import pandas as pd
import numpy as np
import requests
from termcolor import colored as cl
from math import floor
import matplotlib.pyplot as plt

plt.rcParams['figure.figsize'] = (20, 10)
plt.style.use('fivethirtyeight')

def get_historical_data(symbol, start_date = None):
    api_key = open(r'api_key.txt')
    api_url = f'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol={symbol}&apikey={api_key}&outputsize=full'
    raw_df = requests.get(api_url).json()
    df = pd.DataFrame(raw_df[f'Time Series (Daily)']).T
    df = df.rename(columns = {'1. open': 'open', '2. high': 'high', '3. low': 'low', '4. close': 'Close', '5. adjusted close': 'adj close', '6. volume': 'volume'})
    for i in df.columns:
        df[i] = df[i].astype(float)
    df.index = pd.to_datetime(df.index)
    df = df.iloc[::-1].drop(['7. dividend amount', '8. split coefficient'], axis = 1)
    if start_date:
        df = df[df.index >= start_date]
    return df

nflx = get_historical_data('NFLX', '2020-01-01')
nflx.head()

def get_stoch(symbol, k_period, d_period, start_date):
    api_key = open(r'api_key.txt')
    url = f'https://www.alphavantage.co/query?function=STOCH&symbol={symbol}&interval=daily&fastkperiod={k_period}&slowdperiod={d_period}&apikey={api_key}'
    raw = requests.get(url).json()
    df = pd.DataFrame(raw['Technical Analysis: STOCH']).T.iloc[::-1]
    df = df[df.index >= start_date]
    df.index = pd.to_datetime(df.index)
    df = df.astype(float)
    return df['SlowK'], df['SlowD']

nflx['%k'], nflx['%d'] = get_stoch('NFLX', 14, 3, '2020-01-01')
nflx = nflx.dropna()
nflx.head()

def plot_stoch(symbol, price, k, d):
    ax1 = plt.subplot2grid((9, 1), (0,0), rowspan = 5, colspan = 1)
    ax2 = plt.subplot2grid((9, 1), (6,0), rowspan = 3, colspan = 1)
    ax1.plot(price)
    ax1.set_title(f'{symbol} STOCK PRICE')
    ax2.plot(k, color = 'deepskyblue', linewidth = 1.5, label = '%K')
    ax2.plot(d, color = 'orange', linewidth = 1.5, label = '%D')
    ax2.axhline(80, color = 'black', linewidth = 1, linestyle = '--')
    ax2.axhline(20, color = 'black', linewidth = 1, linestyle = '--')
    ax2.set_title(f'{symbol} STOCH')
    ax2.legend()
    plt.show()
    
plot_stoch('NFLX', nflx['Close'], nflx['%k'], nflx['%d'])

def implement_stoch_strategy(prices, k, d):    
    buy_price = []
    sell_price = []
    stoch_signal = []
    signal = 0

    for i in range(len(prices)):
        if k[i] < 20 and d[i] < 20 and k[i] < d[i]:
            if signal != 1:
                buy_price.append(prices[i])
                sell_price.append(np.nan)
                signal = 1
                stoch_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                stoch_signal.append(0)
        elif k[i] > 80 and d[i] > 80 and k[i] > d[i]:
            if signal != -1:
                buy_price.append(np.nan)
                sell_price.append(prices[i])
                signal = -1
                stoch_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                stoch_signal.append(0)
        else:
            buy_price.append(np.nan)
            sell_price.append(np.nan)
            stoch_signal.append(0)
            
    return buy_price, sell_price, stoch_signal
            
buy_price, sell_price, stoch_signal = implement_stoch_strategy(nflx['Close'], nflx['%k'], nflx['%d'])

ax1 = plt.subplot2grid((9, 1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((9, 1), (6,0), rowspan = 3, colspan = 1)
ax1.plot(nflx['Close'], color = 'skyblue', label = 'NFLX')
ax1.plot(nflx.index, buy_price, marker = '^', color = 'green', markersize = 10, label = 'BUY SIGNAL', linewidth = 0)
ax1.plot(nflx.index, sell_price, marker = 'v', color = 'r', markersize = 10, label = 'SELL SIGNAL', linewidth = 0)
ax1.legend(loc = 'upper left')
ax1.set_title('NFLX STOCK PRICE')
ax2.plot(nflx['%k'], color = 'deepskyblue', linewidth = 1.5, label = '%K')
ax2.plot(nflx['%d'], color = 'orange', linewidth = 1.5, label = '%D')
ax2.axhline(80, color = 'black', linewidth = 1, linestyle = '--')
ax2.axhline(20, color = 'black', linewidth = 1, linestyle = '--')
ax2.set_title('NFLX STOCH')
ax2.legend()
plt.show()

position = []
for i in range(len(stoch_signal)):
    if stoch_signal[i] > 1:
        position.append(0)
    else:
        position.append(1)
        
for i in range(len(nflx['Close'])):
    if stoch_signal[i] == 1:
        position[i] = 1
    elif stoch_signal[i] == -1:
        position[i] = 0
    else:
        position[i] = position[i-1]
        
k = nflx['%k']
d = nflx['%d']
close_price = nflx['Close']
stoch_signal = pd.DataFrame(stoch_signal).rename(columns = {0:'stoch_signal'}).set_index(nflx.index)
position = pd.DataFrame(position).rename(columns = {0:'stoch_position'}).set_index(nflx.index)

frames = [close_price, k, d, stoch_signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)

strategy.tail()

nflx_ret = pd.DataFrame(np.diff(nflx['Close'])).rename(columns = {0:'returns'})
stoch_strategy_ret = []

for i in range(len(nflx_ret)):
    try:
        returns = nflx_ret['returns'][i]*strategy['stoch_position'][i]
        stoch_strategy_ret.append(returns)
    except:
        pass
    
stoch_strategy_ret_df = pd.DataFrame(stoch_strategy_ret).rename(columns = {0:'stoch_returns'})

investment_value = 100000
number_of_stocks = floor(investment_value/nflx['Close'][-1])
stoch_investment_ret = []

for i in range(len(stoch_strategy_ret_df['stoch_returns'])):
    returns = number_of_stocks*stoch_strategy_ret_df['stoch_returns'][i]
    stoch_investment_ret.append(returns)

stoch_investment_ret_df = pd.DataFrame(stoch_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(stoch_investment_ret_df['investment_returns']), 2)
profit_percentage = floor((total_investment_ret/investment_value)*100)
print(cl('Profit gained from the STOCH strategy by investing $100k in NFLX : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the STOCH strategy : {}%'.format(profit_percentage), attrs = ['bold']))

def get_benchmark(start_date, investment_value):
    spy = get_historical_data('SPY', start_date)['Close']
    benchmark = pd.DataFrame(np.diff(spy)).rename(columns = {0:'benchmark_returns'})
    
    investment_value = investment_value
    number_of_stocks = floor(investment_value/spy[-1])
    benchmark_investment_ret = []
    
    for i in range(len(benchmark['benchmark_returns'])):
        returns = number_of_stocks*benchmark['benchmark_returns'][i]
        benchmark_investment_ret.append(returns)

    benchmark_investment_ret_df = pd.DataFrame(benchmark_investment_ret).rename(columns = {0:'investment_returns'})
    return benchmark_investment_ret_df

benchmark = get_benchmark('2020-01-01', 100000)
investment_value = 100000
total_benchmark_investment_ret = round(sum(benchmark['investment_returns']), 2)
benchmark_profit_percentage = floor((total_benchmark_investment_ret/investment_value)*100)
print(cl('Benchmark profit by investing $100k : {}'.format(total_benchmark_investment_ret), attrs = ['bold']))
print(cl('Benchmark Profit percentage : {}%'.format(benchmark_profit_percentage), attrs = ['bold']))
print(cl('STOCH Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage), attrs = ['bold']))

Алгоритмическая торговля с индикатором Aroon в Python

Существует множество технических индикаторов, которые можно использовать в исследовательских или торговых целях, но одно общее сходство между ними заключается в том, что все они служат только одной конкретной задаче. Например, RSI можно использовать для определения уровней перекупленности и перепроданности на рынке, индекс изменчивости можно использовать для наблюдения за волатильностью рынка и так далее. Но сегодня мы собираемся изучить индикатор, который можно использовать как для распознавания рыночного тренда, так и для волатильности рынка. Вот, индикатор Аруна! В этой статье мы обсудим, что такое индикатор Aroon, его использование и расчет, а также то, как можно построить торговую стратегию на его основе с помощью python. Без лишних слов, давайте перейдем к статье.

Прежде чем двигаться дальше, если вы хотите протестировать свои торговые стратегии без какого-либо кодирования, для этого есть решение. Это BacktestZone. Это платформа для бесплатного тестирования любого количества торговых стратегий на различных типах торгуемых активов без кодирования. Вы можете использовать инструмент прямо сейчас, используя ссылку здесь: https://www.backtestzone.com/

Индикатор Aroon

Индикатор Aroon, основанный Тушаром Чанде в 1995 году, представляет собой осциллятор импульса, специально разработанный для отслеживания рыночного тренда и его силы. Этот индикатор широко используется трейдерами для определения нового тренда на рынке и соответственно определения потенциальных точек входа и выхода. Будучи осциллятором, значения индикатора Aroon находились в диапазоне от 0 до 100.

Индикатор Aroon состоит из двух компонентов: восходящей линии Aroon и нисходящей линии Aroon. Восходящая линия Aroon измеряет силу восходящего тренда на рынке, а нисходящая линия Aroon измеряет силу нисходящего тренда на рынке. Традиционная настройка индикатора Aroon — либо 14 для краткосрочной, либо 25 для долгосрочной перспективы в качестве периода ретроспективного анализа. В этой статье мы будем использовать 25 в качестве настройки, так как мы будем иметь дело с данными за полтора года. Формула для расчета этих двух линий с 25 в качестве периода ретроспективного анализа выглядит следующим образом:

AROON UP = [ 25 - PERIODS SINCE 25 PERIOD HIGH ] / 25 * [ 100 ]
AROON DOWN = [ 25 - PERIODS SINCE 25 PERIOD LOW ] / 25 * [ 100 ]

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

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

Насколько мне известно, индикатор Aroon можно использовать двумя способами для построения торговой стратегии. Во-первых, это перекрестная торговая стратегия. Эта стратегия показывает сигнал на покупку, если линия вверх Aroon пересекается снизу вверх линии вниз Aroon, точно так же сигнал на продажу выявляется, если линия вверх Aroon движется сверху вниз под линию вниз Aroon. Стратегию кроссовера можно представить следующим образом:

IF P.UP LINE < P.DOWN LINE AND C.UP LINE > C.DOWN LINE --> BUY
IF P.UP LINE > P.DOWN LINE AND C.UP LINE < C.DOWN LINE --> SELL

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

IF AROON UP LINE => 70 AND AROON DOWN LINE <= 30 --> BUY SIGNAL
IF AROON UP LINE <= 30 AND AROON DOWN LINE >= 70 --> SELL SIGNAL

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

Реализация на Python

Часть кодирования подразделяется на различные этапы следующим образом:

1. Importing Packages
2. Extracting Stock Data from Twelve Data
3. Extracting the Aroon Indicator values
4. Aroon Indicator Plot
5. Creating the Trading Strategy
6. Plotting the Trading Lists
7. Creating our Position
8. Backtesting
9. SPY ETF Comparison

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

Шаг 1: Импорт пакетов

Импорт необходимых пакетов в среду python — это шаг, который нельзя пропустить. Первичными пакетами будут Pandas для работы с данными, NumPy для работы с массивами и сложными функциями, Matplotlib для построения графиков и Requests для выполнения вызовов API. Вторичными пакетами будут Math для математических функций и Termcolor для настройки шрифта (необязательно).

Реализация Python:

import pandas as pd
import numpy as np
import requests
import matplotlib.pyplot as plt
from math import floor
from termcolor import colored as cl

plt.style.use('fivethirtyeight')
plt.rcParams['figure.figsize'] = (20, 10)

Теперь, когда мы импортировали все необходимые пакеты в наш python. Давайте извлечем исторические данные Tesla с помощью конечной точки API Twelve Data.

Шаг 2: Извлечение данных из двенадцати данных

На этом этапе мы собираемся извлечь исторические данные об акциях Tesla, используя конечную точку API, предоставленную twelvedata.com. Перед этим заметка о twelvedata.com: Twelve Data — один из ведущих поставщиков рыночных данных, имеющий огромное количество конечных точек API для всех типов рыночных данных. Очень легко взаимодействовать с API, предоставляемыми Twelve Data, и имеет одну из лучших документаций за всю историю. Кроме того, убедитесь, что у вас есть учетная запись на twelvedata.com, только тогда вы сможете получить доступ к своему ключу API (жизненно важный элемент для извлечения данных с помощью API).

Реализация Python:

def get_historical_data(symbol, start_date):
api_key = 'YOUR API KEY'
api_url = f'https://api.twelvedata.com/time_series?symbol={symbol}&interval=1day&outputsize=5000&apikey={api_key}'
raw_df = requests.get(api_url).json()
df = pd.DataFrame(raw_df['values']).iloc[::-1].set_index('datetime').astype(float)
df = df[df.index >= start_date]
df.index = pd.to_datetime(df.index)
return df

tsla = get_historical_data('TSLA', '2020-01-01')
tsla

Пояснение к коду: Первое, что мы сделали, это определили функцию с именем «get_historical_data», которая принимает символ акции («символ») и дату начала исторических данных («start_date») в качестве параметров. Внутри функции мы определяем ключ API и URL-адрес и сохраняем их в соответствующей переменной. Далее мы извлекаем исторические данные в формате JSON с помощью функции «get» и сохраняем их в переменной «raw_df». После выполнения некоторых процессов по очистке и форматированию необработанных данных JSON мы возвращаем их в виде чистого кадра данных Pandas. Наконец, мы вызываем созданную функцию, чтобы извлечь исторические данные Tesla с начала 2020 года и сохранить их в переменной ‘tsla’.

Шаг 3: Извлечение значений индикатора Aroon

На этом этапе мы собираемся извлечь значения индикатора Aroon Tesla с помощью конечной точки API, предоставляемой Twelve Data. Этот шаг почти аналогичен тому, что мы делали на предыдущем шаге.

Реализация Python:

def get_aroon(symbol, lookback, start_date):
api_key = 'YOUR API KEY'
api_url = f'https://api.twelvedata.com/aroon?symbol={symbol}&interval=1day&time_period={lookback}&outputsize=5000&apikey={api_key}'
raw_df = requests.get(api_url).json()
df = pd.DataFrame(raw_df['values']).iloc[::-1].set_index('datetime').astype(float)
df = df[df.index >= start_date]
df.index = pd.to_datetime(df.index)
aroon_up = df['aroon_up']
aroon_down = df['aroon_down']
return aroon_up, aroon_down

tsla['aroon_up'], tsla['aroon_down'] = get_aroon('TSLA', 25, '2020-01-01')
tsla.tail()

Пояснение к коду: Во-первых, мы определяем функцию с именем ‘get_aroon’, которая принимает в качестве параметров символ акции (‘symbol’), период ретроспективного анализа индикатора (‘lookback’) и дату начала данных (‘start_date’). Внутри функции мы сначала назначаем две переменные с именами «api_key» и «url» для хранения ключа API и URL-адреса API соответственно. Используя функцию ‘get’, предоставляемую пакетом Requests, мы вызываем API и сохраняем ответ в переменной ‘raw’. Проделав некоторые манипуляции с данными, мы возвращаем значения как восходящей, так и нижней линий Aroon. Наконец, мы вызываем функцию для извлечения значений индикатора Aroon Tesla.

Шаг 4: График индикатора Aroon

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

Реализация Python:

ax1 = plt.subplot2grid((11,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((11,1), (6,0), rowspan = 4, colspan = 1)
ax1.plot(tsla['close'], linewidth = 2.5, color = '#2196f3')
ax1.set_title('TSLA CLOSE PRICES')
ax2.plot(tsla['aroon_up'], color = '#26a69a', linewidth = 2, label = 'AROON UP')
ax2.plot(tsla['aroon_down'], color = '#ef5350', linewidth = 2, label = 'AROON DOWN')
ax2.legend()
ax2.set_title('TSLA AROON 25')
plt.show()

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

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

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

Шаг 5: Создание торговой стратегии

На этом этапе мы собираемся реализовать обсуждаемую торговую стратегию Aroon Indicator на python.

Реализация Python:

def implement_aroon_strategy(prices, up, down):
buy_price = []
sell_price = []
aroon_signal = []
signal = 0

for i in range(len(prices)):
if up[i] >= 70 and down[i] <= 30:
if signal != 1:
buy_price.append(prices[i])
sell_price.append(np.nan)
signal = 1
aroon_signal.append(signal)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
aroon_signal.append(0)
elif up[i] <= 30 and down[i] >= 70:
if signal != -1:
buy_price.append(np.nan)
sell_price.append(prices[i])
signal = -1
aroon_signal.append(signal)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
aroon_signal.append(0)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
aroon_signal.append(0)

return buy_price, sell_price, aroon_signal

buy_price, sell_price, aroon_signal = implement_aroon_strategy(tsla['close'], tsla['aroon_up'], tsla['aroon_down'])

Пояснение к коду: Во-первых, мы определяем функцию с именем «implement_aroon_strategy», которая принимает цены акций («цена») и линии индикатора Aroon («вверх», «вниз») в качестве параметров.

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

После этого мы реализуем торговую стратегию через фор-цикл. Внутри for-цикла мы передаем определенные условия, и если условия выполняются, соответствующие значения будут добавлены в пустые списки. Если условие для покупки акций будет выполнено, цена покупки будет добавлена в список «buy_price», а значение сигнала будет добавлено как 1, представляющее покупку акций. Точно так же, если условие продажи акций будет выполнено, цена продажи будет добавлена к списку «sell_price», а значение сигнала будет добавлено как -1, представляющее продажу акции.

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

Шаг 6: Построение торговых сигналов

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

Реализация Python:

ax1 = plt.subplot2grid((11,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((11,1), (6,0), rowspan = 4, colspan = 1)
ax1.plot(tsla['close'], linewidth = 2.5, color = '#2196f3')
ax1.plot(tsla.index, buy_price, marker = '^', color = '#26a69a', markersize = 12)
ax1.plot(tsla.index, sell_price, marker = 'v', color = '#ef5350', markersize = 12)
ax1.set_title('TSLA CLOSE PRICES')
ax2.plot(tsla['aroon_up'], color = '#26a69a', linewidth = 2, label = 'AROON UP')
ax2.plot(tsla['aroon_down'], color = '#ef5350', linewidth = 2, label = 'AROON DOWN')
ax2.legend()
ax2.set_title('TSLA AROON 25')
plt.show()

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

Шаг 7: Создание нашей позиции

На этом этапе мы собираемся создать список, в котором будет указано 1, если мы владеем акциями, или 0, если мы не владеем акциями или не держим их.

Реализация Python:

position = []
for i in range(len(aroon_signal)):
if aroon_signal[i] > 1:
position.append(0)
else:
position.append(1)

for i in range(len(tsla['close'])):
if aroon_signal[i] == 1:
position[i] = 1
elif aroon_signal[i] == -1:
position[i] = 0
else:
position[i] = position[i-1]

aroon_up = tsla['aroon_up']
aroon_down = tsla['aroon_down']
close_price = tsla['close']
aroon_signal = pd.DataFrame(aroon_signal).rename(columns = {0:'aroon_signal'}).set_index(tsla.index)
position = pd.DataFrame(position).rename(columns = {0:'aroon_position'}).set_index(tsla.index)

frames = [close_price, aroon_up, aroon_down, aroon_signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)

strategy

Пояснение к коду: Во-первых, мы создаем пустой список с именем «позиция». Мы проходим два цикла for, один из которых предназначен для генерации значений для списка «позиция», чтобы они точно соответствовали длине списка «сигналов». Другой цикл for-loop — это тот, который мы используем для генерации фактических значений позиции. Внутри второго цикла for, мы перебираем значения списка ‘signal’, и добавляются значения списка ‘position’ в зависимости от того, какое условие выполняется. Стоимость позиции остается 1, если мы владеем акциями, или остается 0, если мы продали или не владеем акциями. Наконец, мы делаем некоторые манипуляции с данными, чтобы объединить все созданные списки в один фрейм данных.

Из показанного вывода мы видим, что в первой строке наша позиция по акции осталась 1 (так как сигнал индикатора Aroon не изменился), но наша позиция внезапно изменилась на -1, когда мы продали акцию, когда торговый сигнал индикатора Aroon представляет собой сигнал на продажу (-1). Наша позиция будет оставаться равной 0 до тех пор, пока не произойдут какие-то изменения в торговом сигнале. Теперь пришло время внедрить процесс тестирования на истории!

Шаг 8: Тестирование на истории

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

Реализация Python:

tsla_ret = pd.DataFrame(np.diff(tsla['close'])).rename(columns = {0:'returns'})
aroon_strategy_ret = []

for i in range(len(tsla_ret)):
returns = tsla_ret['returns'][i]*strategy['aroon_position'][i]
aroon_strategy_ret.append(returns)

aroon_strategy_ret_df = pd.DataFrame(aroon_strategy_ret).rename(columns = {0:'aroon_returns'})
investment_value = 100000
number_of_stocks = floor(investment_value/tsla['close'][-1])
aroon_investment_ret = []

for i in range(len(aroon_strategy_ret_df['aroon_returns'])):
returns = number_of_stocks*aroon_strategy_ret_df['aroon_returns'][i]
aroon_investment_ret.append(returns)

aroon_investment_ret_df = pd.DataFrame(aroon_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(aroon_investment_ret_df['investment_returns']), 2)
profit_percentage = floor((total_investment_ret/investment_value)*100)
print(cl('Profit gained from the Aroon strategy by investing $100k in TSLA : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the Aroon strategy : {}%'.format(profit_percentage), attrs = ['bold']))

Выпуск:

Profit gained from the Aroon strategy by investing $100k in TSLA : 55354.46
Profit percentage of the Aroon strategy : 55%

Пояснение к коду: Во-первых, мы вычисляем доходность акций Tesla с помощью функции «diff», предоставляемой пакетом NumPy, и сохраняем ее в виде кадра данных в переменной «tsla_ret». Затем мы передаем цикл for, чтобы перебрать значения переменной «tsla_ret» для расчета доходности, которую мы получили от нашей торговой стратегии индикатора Aroon, и эти значения доходности добавляются к списку «aroon_strategy_ret». Затем мы преобразуем список «aroon_strategy_ret» в кадр данных и сохраняем его в переменной «aroon_strategy_ret_df».

Далее идет процесс тестирования на истории. Мы собираемся протестировать нашу стратегию, вложив сто тысяч долларов США в нашу торговую стратегию. Итак, во-первых, мы храним сумму инвестиций в переменной «investment_value». После этого мы рассчитываем количество акций Tesla, которые мы можем купить, используя сумму инвестиций. Вы можете заметить, что я использовал функцию «пол», предоставляемую математическим пакетом, потому что, деля сумму инвестиций на цену закрытия акций Tesla, он выдает выходные данные с десятичными числами. Количество акций должно быть целым, а не десятичным числом. Используя функцию «пол», мы можем вырезать десятичные дроби. Помните, что функция «пол» намного сложнее, чем функция «круглый». Затем мы проходим цикл for, чтобы найти доходность инвестиций, за которым следуют некоторые задачи по манипулированию данными.

Наконец, мы печатаем общий доход, который мы получили, вложив сто тысяч в нашу торговую стратегию, и выясняется, что мы получили приблизительную прибыль в размере пятидесяти пяти тысяч долларов США за один год. Это неплохо! Теперь давайте сравним нашу доходность с доходностью SPY ETF (ETF, предназначенный для отслеживания индекса фондового рынка S&P 500).

Шаг 9: Сравнение SPY ETF

Этот шаг не является обязательным, но настоятельно рекомендуется, так как мы можем получить представление о том, насколько хорошо наша торговая стратегия работает по сравнению с эталоном (SPY ETF). На этом этапе мы собираемся извлечь данные SPY ETF с помощью функции «get_historical_data», которую мы создали, и сравнить доходность, которую мы получаем от SPY ETF, с доходностью нашей стратегии индикатора Aroon на Tesla.

Реализация Python:

def get_benchmark(start_date, investment_value):
spy = get_historical_data('SPY', start_date)['close']
benchmark = pd.DataFrame(np.diff(spy)).rename(columns = {0:'benchmark_returns'})

investment_value = investment_value
number_of_stocks = floor(investment_value/spy[-1])
benchmark_investment_ret = []

for i in range(len(benchmark['benchmark_returns'])):
returns = number_of_stocks*benchmark['benchmark_returns'][i]
benchmark_investment_ret.append(returns)

benchmark_investment_ret_df = pd.DataFrame(benchmark_investment_ret).rename(columns = {0:'investment_returns'})
return benchmark_investment_ret_df

benchmark = get_benchmark('2020-01-01', 100000)

investment_value = 100000
total_benchmark_investment_ret = round(sum(benchmark['investment_returns']), 2)
benchmark_profit_percentage = floor((total_benchmark_investment_ret/investment_value)*100)
print(cl('Benchmark profit by investing $100k : {}'.format(total_benchmark_investment_ret), attrs = ['bold']))
print(cl('Benchmark Profit percentage : {}%'.format(benchmark_profit_percentage), attrs = ['bold']))
print(cl('Aroon Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage), attrs = ['bold']))

Выпуск:

Benchmark profit by investing $100k : 21090.3
Benchmark Profit percentage : 21%
Aroon Strategy profit is 34% higher than the Benchmark Profit

Пояснение к коду: Код, используемый на этом этапе, почти аналогичен тому, который использовался на предыдущем этапе тестирования на истории, но вместо того, чтобы инвестировать в Tesla, мы инвестируем в SPY ETF, не реализуя никаких торговых стратегий. Из вывода мы видим, что наша стратегия торговли по индикатору Aroon превзошла SPY ETF на 34%. Это здорово!

Заключение

После долгого процесса дробления как теории, так и части кодирования, мы успешно узнали, что такое индикатор Aroon и как построить торговую стратегию на его основе. Индикатор Aroon всегда оставался моим личным фаворитом, поскольку он помогает выполнять две работы одновременно (определять тенденции, волатильность), и он также уникален. Несмотря на то, что мы превзошли доходность SPY ETF, все еще есть возможности для улучшения работы:

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

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

Полный код:

import pandas as pd
import numpy as np
import requests
import matplotlib.pyplot as plt
from math import floor
from termcolor import colored as cl

plt.style.use('fivethirtyeight')
plt.rcParams['figure.figsize'] = (20, 10)

def get_historical_data(symbol, start_date):
api_key = 'YOUR API KEY'
api_url = f'https://api.twelvedata.com/time_series?symbol={symbol}&interval=1day&outputsize=5000&apikey={api_key}'
raw_df = requests.get(api_url).json()
df = pd.DataFrame(raw_df['values']).iloc[::-1].set_index('datetime').astype(float)
df = df[df.index >= start_date]
df.index = pd.to_datetime(df.index)
return df

tsla = get_historical_data('TSLA', '2020-01-01')
print(tsla)

def get_aroon(symbol, lookback, start_date):
api_key = 'YOUR API KEY'
api_url = f'https://api.twelvedata.com/aroon?symbol={symbol}&interval=1day&time_period={lookback}&outputsize=5000&apikey={api_key}'
raw_df = requests.get(api_url).json()
df = pd.DataFrame(raw_df['values']).iloc[::-1].set_index('datetime').astype(float)
df = df[df.index >= start_date]
df.index = pd.to_datetime(df.index)
aroon_up = df['aroon_up']
aroon_down = df['aroon_down']
return aroon_up, aroon_down

tsla['aroon_up'], tsla['aroon_down'] = get_aroon('TSLA', 25, '2020-01-01')
print(tsla.tail())

ax1 = plt.subplot2grid((11,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((11,1), (6,0), rowspan = 4, colspan = 1)
ax1.plot(tsla['close'], linewidth = 2.5, color = '#2196f3')
ax1.set_title('TSLA CLOSE PRICES')
ax2.plot(tsla['aroon_up'], color = '#26a69a', linewidth = 2, label = 'AROON UP')
ax2.plot(tsla['aroon_down'], color = '#ef5350', linewidth = 2, label = 'AROON DOWN')
ax2.legend()
ax2.set_title('TSLA AROON 25')
plt.show()

def implement_aroon_strategy(prices, up, down):
buy_price = []
sell_price = []
aroon_signal = []
signal = 0

for i in range(len(prices)):
if up[i] >= 70 and down[i] <= 30:
if signal != 1:
buy_price.append(prices[i])
sell_price.append(np.nan)
signal = 1
aroon_signal.append(signal)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
aroon_signal.append(0)
elif up[i] <= 30 and down[i] >= 70:
if signal != -1:
buy_price.append(np.nan)
sell_price.append(prices[i])
signal = -1
aroon_signal.append(signal)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
aroon_signal.append(0)
else:
buy_price.append(np.nan)
sell_price.append(np.nan)
aroon_signal.append(0)

return buy_price, sell_price, aroon_signal

buy_price, sell_price, aroon_signal = implement_aroon_strategy(tsla['close'], tsla['aroon_up'], tsla['aroon_down'])

ax1 = plt.subplot2grid((11,1), (0,0), rowspan = 5, colspan = 1)
ax2 = plt.subplot2grid((11,1), (6,0), rowspan = 4, colspan = 1)
ax1.plot(tsla['close'], linewidth = 2.5, color = '#2196f3')
ax1.plot(tsla.index, buy_price, marker = '^', color = '#26a69a', markersize = 12)
ax1.plot(tsla.index, sell_price, marker = 'v', color = '#ef5350', markersize = 12)
ax1.set_title('TSLA CLOSE PRICES')
ax2.plot(tsla['aroon_up'], color = '#26a69a', linewidth = 2, label = 'AROON UP')
ax2.plot(tsla['aroon_down'], color = '#ef5350', linewidth = 2, label = 'AROON DOWN')
ax2.legend()
ax2.set_title('TSLA AROON 25')
plt.show()

position = []
for i in range(len(aroon_signal)):
if aroon_signal[i] > 1:
position.append(0)
else:
position.append(1)

for i in range(len(tsla['close'])):
if aroon_signal[i] == 1:
position[i] = 1
elif aroon_signal[i] == -1:
position[i] = 0
else:
position[i] = position[i-1]

aroon_up = tsla['aroon_up']
aroon_down = tsla['aroon_down']
close_price = tsla['close']
aroon_signal = pd.DataFrame(aroon_signal).rename(columns = {0:'aroon_signal'}).set_index(tsla.index)
position = pd.DataFrame(position).rename(columns = {0:'aroon_position'}).set_index(tsla.index)

frames = [close_price, aroon_up, aroon_down, aroon_signal, position]
strategy = pd.concat(frames, join = 'inner', axis = 1)

strategy.head()

print(strategy[43:48])

tsla_ret = pd.DataFrame(np.diff(tsla['close'])).rename(columns = {0:'returns'})
aroon_strategy_ret = []

for i in range(len(tsla_ret)):
returns = tsla_ret['returns'][i]*strategy['aroon_position'][i]
aroon_strategy_ret.append(returns)

aroon_strategy_ret_df = pd.DataFrame(aroon_strategy_ret).rename(columns = {0:'aroon_returns'})
investment_value = 100000
number_of_stocks = floor(investment_value/tsla['close'][-1])
aroon_investment_ret = []

for i in range(len(aroon_strategy_ret_df['aroon_returns'])):
returns = number_of_stocks*aroon_strategy_ret_df['aroon_returns'][i]
aroon_investment_ret.append(returns)

aroon_investment_ret_df = pd.DataFrame(aroon_investment_ret).rename(columns = {0:'investment_returns'})
total_investment_ret = round(sum(aroon_investment_ret_df['investment_returns']), 2)
profit_percentage = floor((total_investment_ret/investment_value)*100)
print(cl('Profit gained from the Aroon strategy by investing $100k in TSLA : {}'.format(total_investment_ret), attrs = ['bold']))
print(cl('Profit percentage of the Aroon strategy : {}%'.format(profit_percentage), attrs = ['bold']))

def get_benchmark(start_date, investment_value):
spy = get_historical_data('SPY', start_date)['close']
benchmark = pd.DataFrame(np.diff(spy)).rename(columns = {0:'benchmark_returns'})

investment_value = investment_value
number_of_stocks = floor(investment_value/spy[-1])
benchmark_investment_ret = []

for i in range(len(benchmark['benchmark_returns'])):
returns = number_of_stocks*benchmark['benchmark_returns'][i]
benchmark_investment_ret.append(returns)

benchmark_investment_ret_df = pd.DataFrame(benchmark_investment_ret).rename(columns = {0:'investment_returns'})
return benchmark_investment_ret_df

benchmark = get_benchmark('2020-01-01', 100000)

investment_value = 100000
total_benchmark_investment_ret = round(sum(benchmark['investment_returns']), 2)
benchmark_profit_percentage = floor((total_benchmark_investment_ret/investment_value)*100)
print(cl('Benchmark profit by investing $100k : {}'.format(total_benchmark_investment_ret), attrs = ['bold']))
print(cl('Benchmark Profit percentage : {}%'.format(benchmark_profit_percentage), attrs = ['bold']))
print(cl('Aroon Strategy profit is {}% higher than the Benchmark Profit'.format(profit_percentage - benchmark_profit_percentage), attrs = ['bold']))

Использование дельта-нейтральных стратегий для стабильной безрисковой прибыли

Фото Арта Рачена с сайта Unsplash

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

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

Что такое дельта-нейтральная количественная стратегия?

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

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

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

Общий подход, рыночная неэффективность и рациональное обоснование

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

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

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

В следующих разделах мы подробно опишем реализованные P&L на каждом этапе.

Сценарий 1: Рынок начинается в контанго и никогда не достигает бэквордации

Мы предполагаем, что рынок начинается в момент t₁ в контанго (будущая цена выше спотовой цены актива). Для того, чтобы извлечь выгоду из сложившейся ситуации:

  • Мы шортим фьючерсный контракт на t₁ :
    P&L(T) = Будущая цена (t₁) — Цена актива (T)
  • Покупаем базовый актив по t₁:
    P&L (T) = Цена актива (T) — Цена актива (t₁)

Таким образом, мы рассчитываем общую прибыль, суммируя 2 компонента P&L:

Итого P&L(T) = Будущая цена (t₁) — Цена актива (t₁) >0

Как видно из предыдущего уравнения, одновременно открывая короткую позицию по фьючерсному контракту и покупая базовый актив, мы фактически можем гарантировать положительную прибыль при t₁ без какого-либо риска или подверженности, поскольку общий P&L при T всегда положителен и не зависит от колебаний цены базового актива.

Сценарий 2: Рынок начинается в контанго и достигает бэквордации

Мы предполагаем, что рынок начинается с t в контанго (будущая цена выше спотовой цены актива) и достигает в t₂ бэквордации (будущая цена ниже спотовой цены актива). Для того, чтобы извлечь выгоду из сложившейся ситуации:

  • Шортим фьючерсный контракт на t₁:
    P&L(T)= Будущая цена (t₁) — Цена актива (T)
  • Мы открываем длинную позицию по фьючерсному контракту на t₂:
    P&L(T) = Цена актива (T) — Будущая цена (t₂)
  • Мы покупаем базовый актив по t₁ и продаем его по t₂:
    P&L(T) = Цена актива (t₂) — Цена актива (t₁)

Подведем итоги:

  • A = Будущая цена (t₁) — Цена актива (t₁)
  • B = Цена актива (t₂) — Будущая цена (t₂)
  • Итого P&L(T) = A + B

Если мы внимательно посмотрим на общее уравнение P&L:

  • A > 0 (так как рынок находится в контанго при t₁)
  • B > 0 (так как рынок находится в бэквордации при t₂)

В результате мы можем гарантировать положительную прибыль как при t, так и при t₂ без какого-либо риска или подверженности, поскольку общий P&L при T всегда положителен и не зависит от колебаний цены базового актива.

Сценарий 3: Обобщение

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

Реализация сигнала

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

Это соотношение может быть определено в любой момент времени t как:

Для наглядности ниже мы построим график для одного из фьючерсных контрактов, котирующихся на Binance:

Соотношение фьючерсов к споту для фьючерсного контракта XRPUSDT на июнь 2021 года

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

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

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

Результаты и основные наблюдения

Мы используем эту стратегию с 2020 года в нашей реальной торговой среде. Ниже вы найдете график совокупной доходности.

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

Заключение

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

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

Однако важно отметить, что стратегия несет в себе и риски. Сроки имеют решающее значение, так как расхождения в ценах могут быстро сократиться или исчезнуть. Кроме того, рыночные условия могут меняться, снижая рентабельность некоторых неэффективных компаний. Тщательная практика управления рисками, тщательные исследования и глубокое понимание базовых активов и динамики рынка необходимы для успешной реализации этой стратегии. Для реализации этой простой торговой стратегии также требуется надежная ИТ-инфраструктура, поскольку она необходима для мониторинга рынка в режиме 24/7 для выявления наилучших недостатков. Если вам интересно узнать больше о нашей текущей инфраструктуре, мы подробно описали ее в отдельной статье: Проектирование расширенной инфраструктуры алготрейдинга с помощью AWS: от сбора данных до исполнения ордеров

Приложение и технические соображения

Для этой стратегии мы используем квартальные фьючерсные контракты Binance COIN-M.

Квартальные фьючерсные контракты рассчитываются в дату экспирации, т.е. в последнюю пятницу каждого календарного квартала (март, июнь, сентябрь, декабрь) в 08:00:00 UTC. Квартальные фьючерсные контракты начинают торговаться за 6 месяцев до даты их экспирации.

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

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

Высокочастотная стратегия получения ликвидности

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

В этом руководстве мы расскажем вам, как создать алгоритмическую торговую стратегию, основанную на правилах. Мы также покажем вам, как вы можете рассчитать торговые показатели, такие как PnL, онлайн с помощью канала Databento в режиме реального времени. Этот пример может быть адаптирован к сценариям высокочастотной торговли (HFT) и среднечастотной торговли.

Прежде чем разбирать стратегию, давайте объясним некоторую терминологию:

Характеристика: Любая базовая независимая переменная, которая, как считается, имеет некоторую прогностическую ценность. Это следует за номенклатурой машинного обучения; Другие могут называть это предиктором или регрессором в статистической или эконометрической среде.

Торговое правило: Жестко закодированное торговое решение. Например, «Если по лучшему офферу остался только один ордер, снимите предложение; Если по лучшей ставке остался только один ордер, нажмите на ставку». Торговое правило может быть жестко запрограммированным торговым решением, принятым, когда значение характеристики превышает определенный порог.

Стратегия, основанная на правилах: Стратегия, основанная на торговых правилах, а не на альфа-версиях, основанных на моделях.

Liquidity-taking strategy (Стратегия получения ликвидности) — Стратегия, которая берет ликвидность путем пересечения спреда агрессивными или рыночными ордерами.

Высокочастотная стратегия: Стратегия, характеризующаяся большим количеством сделок. В обществе нет единого мнения о том, что это означает, но такие стратегии, как правило, показывают высокий направленный оборот, ADV не менее 20 б.. и небольшую максимальную позицию. Важно отметить, что низкая задержка и короткий период удержания не являются обязательными условиями, но на практике большинство таких стратегий демонстрируют резкое снижение PnL до 15 микросекунд от провода к проводу.

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

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

Асимметрия книг и правило торговли

Простейший тип книжного признака называется книжным асимметрией, который представляет собой дисбаланс между глубиной спроса в покое (vb) и глубиной покоя ask (va) в верхней части книги. Мы можем сформулировать это как некоторую разницу между vb и va. Их удобно масштабировать по порядку величины, поэтому мы возьмем их логарифмические различия.

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

Мы можем ввести торговое правило, которое покупает, когда эта функция превышает некоторый порог наклона k, и продает, когда она опускается ниже некоторого порога -k.

Минимальный объем заказа

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

Минимальная сумма ордера зависит от торговой платформы или рынка, на котором вы находитесь. Для примера стратегии мы будем использовать фьючерсный контракт E-mini S&P 500 (ES) в качестве примера и торговать клипами по 1 контракту.

Комиссий

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

Лимиты позиций и рисков

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

Реализация

Вот параметры, которые у нас есть на данный момент. Мы используем инструмент с сырым символом ESU3 (контракт с истекающим сентябрем). Вам нужно будет заменить его желаемым символом.import math
from pprint import pprint
from dataclasses import dataclass, field
from decimal import Decimal
from typing import Optional, List

import pandas as pd
import databento as db

@dataclass(frozen=True)
class Config:
# Databento API Key
api_key: Optional[str] = None # «YOUR_API_KEY»

# Alpha threshold to buy/sell, k
skew_threshold: float = 1.7

# Databento dataset
dataset: str = «GLBX.MDP3»

# Instrument information
symbol: str = «ES.c.0»
stype_in: str = «continuous»
point_value: Decimal = Decimal(«50») # $50 per index point

# Fees
venue_fees_per_side: Decimal = Decimal(«0.39»)
clearing_fees_per_side: Decimal = Decimal(«0.05»)

@property
def fees_per_side(self) -> Decimal:
return self.venue_fees_per_side + self.clearing_fees_per_side

# Position limit
position_max: int = 10

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

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

Такой онлайн-алгоритм выгоден, так как требования к времени выполнения и памяти не увеличиваются с количеством используемых точек данных или количеством заказов, размещенных в симуляции.@dataclass
class Strategy:
# Static configuration
config: Config

# Current position, in contract units
position: int = 0
# Number of long contract sides traded
buy_qty: int = 0
# Number of short contract sides traded
sell_qty: int = 0

# Total realized buy price
real_total_buy_px: Decimal = Decimal(«0»)
# Total realized sell price
real_total_sell_px: Decimal = Decimal(«0»)

# Total buy price to liquidate current position
theo_total_buy_px: Decimal = Decimal(«0»)
# Total sell price to liquidate current position
theo_total_sell_px: Decimal = Decimal(«0»)

# Total fees paid
fees: Decimal = Decimal(«0»)

# List to track results
results: List[object] = field(default_factory=list)

def run(self) -> None:
client = db.Live(self.config.api_key)
client.subscribe(
dataset=self.config.dataset,
schema=»mbp-1″,
stype_in=self.config.stype_in,
symbols=[self.config.symbol],
)
for record in client:
if isinstance(record, db.MBP1Msg):
self.update(record)

def update(self, record: db.MBP1Msg) -> None:
ask_size = record.levels[0].ask_sz
bid_size = record.levels[0].bid_sz
ask_price = record.levels[0].ask_px / Decimal(«1e9»)
bid_price = record.levels[0].bid_px / Decimal(«1e9»)

# Calculate skew feature
skew = math.log10(bid_size) — math.log10(ask_size)

# Buy/sell based when skew signal is large
if (
skew > self.config.skew_threshold
and self.position < self.config.position_max
):
self.position += 1
self.buy_qty += 1
self.real_total_buy_px += ask_price
self.fees += self.config.fees_per_side
elif (
skew < -self.config.skew_threshold
and self.position > -self.config.position_max
):
self.position -= 1
self.sell_qty += 1
self.real_total_sell_px += bid_price
self.fees += self.config.fees_per_side

# Update prices
# Fill prices are based on BBO with assumed zero latency
# In practice, fill prices will likely be worse
if self.position == 0:
self.theo_total_buy_px = Decimal(«0»)
self.theo_total_sell_px = Decimal(«0»)
elif self.position > 0:
self.theo_total_sell_px = bid_price * abs(self.position)
elif self.position < 0:
self.theo_total_buy_px = ask_price * abs(self.position)

# Compute PnL
theo_pnl = (
self.config.point_value
* (
self.real_total_sell_px
+ self.theo_total_sell_px
— self.real_total_buy_px
— self.theo_total_buy_px
)
— self.fees
)

# Print & store results
result = {
«ts_strategy»: record.pretty_ts_recv,
«bid»: bid_price,
«ask»: ask_price,
«skew»: skew,
«position»: self.position,
«trade_ct»: self.buy_qty + self.sell_qty,
«fees»: self.fees,
«pnl»: theo_pnl,
}
pprint(result)
self.results.append(result)

if __name__ == «__main__»:
config = Config()
strategy = Strategy(config=config)
try:
strategy.run()
except KeyboardInterrupt:
pass
df = pd.DataFrame.from_records(strategy.results, index=»ts_strategy»)
df.to_csv(«strategy_log.csv»)

Результаты

Дальнейшие улучшения

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

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

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

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

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

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

Источник

Источник

Источник

Источник

Источник