Cтратегии торговли криптовалютой

Часть 1: Импульсная торговля криптовалютой

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

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

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

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

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

— Стадное поведение на криптовалютных рынках. Имитация решений друг друга без учета фундаментальных факторов приводит к значительному стадному поведению на криптовалютных рынках. Это приводит к спекулятивным пузырям, во время которых некоторые токены могут вырасти в цене в 100, 1000 или даже 100 000 раз! Если вы читаете эту книгу, я думаю, что вы слышали о SHIBA, PEPE или DOGE.

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

— Короткая концентрация внимания участников рынка. В отличие от традиционных рынков, криптовалюта всегда открыта 24/7 в течение года. То, что рынки постоянно активны, означает, что криптотрейдеры могут упустить и упускают много информации. Поэтому многие криптоинвесторы реагируют на развитие рынка со значительным запаздыванием, что создает возможности для импульсных трейдеров.

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

Часть 2: Торговая стратегия Crypto Momentum на Python

Стратегия с нуля

Во-первых, импортируем все библиотеки, которые мы будем использовать.

Запросы на импорт

Импорт JSON

Импорт Panda как PD

Импорт даты и времени в формате DT

Из heapq import nlargest

Из functools import reduce

Импорт numpy как NP

Из статистики импорта Scipy

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

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

def create_df():

пара = «USDT»

root_url = ‘https://api.binance.com/api/v3/klines’

interval = ‘1d’

close_prices = пд. Кадр данных()

Для i в lst:

url = root_url + ‘?symbol=’ + i + pair + ‘&interval=’ + interval

data = json.loads(requests.get(url).text)

Если ‘msg’ в данных:

проходить

еще:

df = pd. DataFrame(данные)

df.columns = [‘open_time’,

‘o’, ‘h’, ‘l’, ‘c’, ‘v’,

‘close_time’, ‘qav’, ‘num_trades’,

‘taker_base_vol’, ‘taker_quote_vol’, ‘игнорировать’]

df.index = [dt.datetime.fromtimestamp(x/1000.0) для x в df.close_time]

close_price = df[‘c’]

close_prices[i] = close_price

close_prices = close_prices.apply(pd.to_numeric)

Возврат close_prices

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

Параметры

Даты и, следовательно, цены, скорее всего, будут отличаться, но вы поняли идею. Теперь давайте определимся с параметрами стратегии. Первое, что мы должны решить, это то, сколько дней мы будем рассматривать данные, что и делает ретроспективный анализ. Когда исследователи тестируют импульсную стратегию на фондовых рынках, они обычно оглядываются на 3-12 показателей за последние месяцы, которые упали в прошлом месяце. Но поскольку криптовалюта более динамична, чем фондовый рынок, весьма вероятно, что любой эффект импульса будет сведен на нет в течение 90 дней. Вот почему для этой модели я решил использовать 35 дней. Мы отбросим данные за последние 14 дней, и это то, что делает строка last_days = 14.

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

Экспоненциальная регрессия

Теперь, когда мы получили исторические данные от Binance и определили параметры модели, вы можете спросить: «Хорошо, а как нам выбрать пять лучших монет недели?». Это хороший вопрос, и ответ на него может иметь большое значение для хорошей и плохой модели. Для этой стратегии я буду использовать метрику импульса, разработанную Андреасом Кленовым в его книге «Trading Evolved». Кстати, это замечательная вводная книга по построению торговых стратегий на Python.

Сначала я должен объяснить, почему мы не смотрим только на процентную доходность, чтобы измерить динамику. Возьмем высоковолатильную монету. Новость об этом только что вышла и монета стремительно растет. Это не означает, что рассматриваемая криптовалюта на самом деле сильна; Он просто отреагировал на новость, после чего может вернуть большую часть своего дохода на рынок. Теперь вы видите проблему, я надеюсь. Если мы выберем процентную доходность в качестве метрики, в нашем портфеле будут доминировать высоковолатильные цифровые активы. Но дело не в этом. Мы должны думать не только о производительности, но и о пути этого выступления.

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

Но вот в чем проблема. Возьмем две монеты с высокой и низкой ценой, например, $MKR, которая торгуется по 4-значной цене, и $SHIBA, которая торгуется по такой крошечной цене, что количество нулей в ее цене кажется бесконечным. Допустим, мы запускаем нашу модель, и обе криптовалюты показывают наклон регрессии в 0,1 доллара (или 0,1 USDT, это не имеет значения для нашего обсуждения). Несмотря на то, что наклон одинаков для жетонов, он означает разные вещи для $MKR и $SHIBA. Поскольку 0,1 — это ничтожный процент от $MKR цены, эта модель подразумевает, что Maker растет очень медленно. И наоборот, наклон $SHIBA велик по отношению к его цене, что означает, что $SHIBA сейчас торгуется с большим импульсом. Таким образом, использование модели, которая дает наклон в абсолютных числах USD или USDT, бесполезно и может привести к неправильным инвестиционным решениям.

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

(1+0,00634)365–1 = 0,2603

Это означает, что мы имеем дело с цифровым активом с положительной динамикой примерно 26% в год. Причина, по которой мы используем 365 градусов, заключается в том, что, поскольку наши данные являются ежедневными, результат модели также будет ежедневным. Поэтому, чтобы пересчитать его в годовом исчислении, мы возводим его в степень 365, потому что крипторынки открыты 24/7, 365 дней в году.

Теперь о коэффициенте детерминации части. Эта метрика, более лаконично называемая R2, измеряет, насколько хорошо линия регрессии соответствует данным. Другими словами, он показывает, насколько хорошо вариация в нашей независимой переменной (переменных) объясняет вариацию в зависимой переменной. Чем выше R2, тем лучше. Добавляем коэффициент детерминации, чтобы наказать более волатильные токены. Постепенный рост цифровых активов будет иметь более плавный тренд и, следовательно, более высокий R2.

Приведенный ниже код делает все то, что мы обсуждали выше.

Def Slope(ts):

ts = ts.dropna()

x = np.arange(len(ts))

log_ts = np.log(тс)

наклон, пересечение, r_value, p_value, std_err = stats.linregress(x, log_ts)

annualized_slope = (np.мощность(np.exp(наклон), 365) -1) *100

возврат annualized_slope * (r_value ** 2)

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

Я создал PDF-файл, объясняющий эту модель, и приложил полный код Python.

Чтобы получить код в формате PDF и Python, перейдите по следующей ссылке:

https://fmiren.gumroad.com/l/zwnkt?_gl=1*goxinf*_ga*MTgyNzMyOTE1NC4xNzAzNjU2ODAw*_ga_6LJN6D94N6*MTcwMzkzMjA3Ny4xNC4xLjE3MDM5MzIwNzkuMC4wLjA.

Стратегии торговли криптопарами

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

Затем мы создаем список, содержащий несколько монет, торгуемых на Binance.

Конечно, вы можете добавить или удалить столько элементов из этого массива, сколько пожелаете.

В следующих строках мы определяем переменные pair, root_url интервал, которые позволяют нам получать исторические данные из Binance. Затем мы считываем данные со следующей строкой:

data = json.loads(requests.get(url).text)

Когда вы посмотрите на содержимое переменной данных, вы увидите что-то вроде этого:

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

df = pd.DataFrame(data)

Теперь наш df выглядит так:

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

Мы переименуем наши столбцы с помощью следующего кода:

df.columns = ['open_time',
'o', 'h', 'l', 'c', 'v',
'close_time', 'qav', 'num_trades',
'taker_base_vol', 'taker_quote_vol', 'ignore']

Наш фрейм данных теперь в понятной человеку форме.

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

df.index = [dt.datetime.fromtimestamp(x/1000.0) for x in df.close_time]

Это наша функция create_df() которую мы подробно объяснили ниже

def create_df():
pair = "USDT"
root_url = 'https://api.binance.com/api/v3/klines'
interval = '1d'
close_prices = pd.DataFrame()
for i in lst:
url = root_url + '?symbol=' + i + pair + '&interval=' + interval
data = json.loads(requests.get(url).text)
if 'msg' in data:
pass
else:
df = pd.DataFrame(data)
df.columns = ['open_time',
'o', 'h', 'l', 'c', 'v',
'close_time', 'qav', 'num_trades',
'taker_base_vol', 'taker_quote_vol', 'ignore']
df.index = [dt.datetime.fromtimestamp(x/1000.0) for x in df.close_time]
close_price = df['c']
close_prices[i] = close_price
close_prices = close_prices.apply(pd.to_numeric)
return close_prices

Вызов этой функции вернет фрейм данных, содержащий исторические данные о ценах от Binance на указанные нами токены.

Итак, наш фрейм данных содержит исторические данные с Binance в удобочитаемом формате. Следующим шагом является расчет ежедневной доходности монет, чтобы мы могли вычислить корреляцию доходности. pct_change() в Pandas — это, пожалуй, самый питоновый способ сделать это.

def calculate_daily_return(df):
daily_return = df.pct_change(1)
return daily_return

Следующая функция берет фрейм данных ежедневных доходностей и создает корреляционную матрицу доходности. Давайте пройдемся по строкам. Сначала создадим корреляционную матрицу с помощью функции corr(). Затем мы преобразуем наш датафрейм в большой столбец с помощью функции melt().

Мы должны отбросить все корреляции со значением 1, так как это корреляции монет с самими собой; Нас эти не интересуют. Нас интересуют корреляции монет с другими монетами. Мы также игнорируем корреляции со значением ниже 0,75, поскольку они не указывают на сильную связь между двумя активами. Я не утверждаю, что 0,75 намного лучше, чем любой другой порог, но мы должны с чего-то начать. Наконец, столбец сортируется по убыванию, который хранится в sorted_corrs переменной. Функция возвращает sorted_corrs.

def correlations(df):
corr_matrix = df.corr()
correlations_df = corr_matrix.melt()
correlations_df = correlations_df.loc[(correlations_df["value"] != 1.0) & (correlations_df["value"] >= 0.75)]
sorted_corrs = correlations_df.sort_values(ascending=False, by="value")
return sorted_corrs

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

Скажем, из-за ажиотажа цена токена MANA значительно выросла. Мы выяснили, что за последние 30 дней (период ретроспективного анализа) MANA коррелирует как с ENJ, так и с SAND. Всплеск привел к тому, что MANA торговалась выше исторических спредов ENJ-MANA и SAND-MANA. Это означает, что мы должны занять короткую позицию по обеим парам, т.е. в первой сделке мы покупаем ENJ и продаем MANA, а во второй сделке покупаем SAND и продаем MANA. Мы будем рисковать большими деньгами с MANA, чем с другими монетами. Если у нас есть $10 000 и мы выделяем $1 000 на каждую сделку, это означает, что мы будем торговать каждой ногой каждой пары с $500. Но поскольку MANA находится в двух сделках, мы рискнем $1,000 именно этой монетой. Еще раз повторю — это не то, чем мы хотим заниматься. Так что этого следует избегать. Если вы пройдетесь по коду, вы увидите, что это то, что делает сценарий.

Пары монет хранятся в виде кортежей в монетах, которые возвращает функция.

# select 10 most highly correlated pairs
def unique_pairs(df):
# create an empty list where we'll store pairs of correlated tokens
coins = []
# temporary array
out = []
high_corrs_array = list(df["variable"])
for i in range(0, len(df), 2):
if high_corrs_array[i] not in out and high_corrs_array[i+1] not in out:
tupl = (df.iloc[i]["variable"], df.iloc[i+1]["variable"])
coins.append(tupl)
out = [item for t in coins for item in t]
if len(coins) == 10:
break
return coins

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

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

Модель торговли криптовалютными парами

Во-первых, давайте посмотрим на код.

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

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

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

Теперь мы можем пройтись по нашему списку пар монет. Идея проста. Допустим, мы находимся в 31-м ряду. Мы вычисляем корреляционную матрицу для нашего фрейма данных и выбираем пары монет, которые показали наибольшую корреляцию за последние 30 дней. После того, как мы выбрали 10 пар монет, мы проверяем, есть ли существенное отклонение от среднего спреда между двумя ногами пары. Допустим, одна из пар в нашем списке (ENJ, MANA). Мы рассчитываем спред между ценами этих двух монет за последние 30 дней. На 31-й день мы проверяем, значительно ли сейчас спред выше или ниже нашего исторического 30-дневного спреда. Если текущий спред выше, мы ставим, что спред вернется к своему историческому среднему значению. Это означает, что мы шортим спред, т.е. мы шортим первую монету и одновременно открываем длинную позицию по второй. И наоборот, если спред значительно ниже исторического спреда, мы открываем длинную позицию по первой монете и одновременно открываем короткую позицию по второй.

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

Как мы можем его улучшить?

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

Некоторые изменения, которые вы можете рассмотреть, включают, но не ограничиваются:

1) Изменить базу пары с USDT на BTC. Например, вместо того, чтобы смотреть на спред между ETHUSDT и DOTUSDT, мы можем проанализировать пару ETHBTC и DOTBTC. Я думаю, что это может значительно улучшить эффективность стратегии. Почему? Потому что рынок криптовалют очень волатилен, и монеты с базой USDT могут находиться в тренде в течение длительного периода времени. Происходит это просто из-за того, что одна нога пары является стейблкоином и не так сильно двигается. Это нежелательно, если мы хотим, чтобы пары токенов вернулись к среднему значению. Но если мы свяжем монету с BTC, а не со стейблкоином, таким как USDT, это, возможно, приведет к более среднему поведению, потому что в этом случае одной из ветвей пары является BTC, который сам по себе может значительно колебаться.

2) Вы можете играть со значением k. Это параметр, который, умноженный на стандартное отклонение спреда, показывает, насколько текущий спред отклонился от среднего спреда. Значение 1 даст много сигналов, но также и много ложных сигналов. Более высокие значения будут давать меньшее, но, надеюсь, более точные сигналы.

3) Можно исключить пары, где ожидается дальнейшее отклонение цены. Если спред < (среднее — 4 * std) или спред > (среднее значение + 4 * std), то можно предположить, что эти пары находятся в тренде и они могут не вернуться к своему среднему значению. Дополнительные сведения см. в разделе # https://collective.flashbots.net/t/frp-35-pair-trading-opportunities-as-a-form-of-mev/2887

Источник