Расширенные исследования платформ Анализ данных Python и стратегический тест

Автор:Нинабадасс., Создано: 2022-04-13 09:12:47, Обновлено: 2022-04-28 11:06:13

Расширенные исследования платформ Анализ данных Python и стратегия Backtest.ipynb

Расширенные исследования платформ

FMZ имеет встроенный ноутбук jupyter, который помогает пользователям ознакомиться с API платформы и проводить исследования стратегии, и поддерживает среды обучения Python3 C++11/17 и Javascript. Notebook+Python является очень мощным инструментом, который практически незаменим для анализа данных и исследования стратегии. Хотя бэкстест, который поставляется с платформой FMZ, очень полезен, он не подходит для стратегий со сложными и большими объемами данных. В этой статье будут представлены некоторые передовые навыки использования ноутбука jupyter и реализованы бэкстесты стратегий случайной торговли парами и многопарной торговли.

Использование Jupyter

Внутри FMZ можно использовать исследовательскую среду, но сеть неудобна. Рекомендуется установить на собственное устройство анаконда3, с ноутбуком и часто используемыми связанными библиотеками для математических вычислений; он может делиться локальной сетевой средой и иметь лучшую производительность. Также рекомендуется использовать Google colab. Хотя есть некоторые ограничения на хранение, он бесплатный и мощный, подходящий для исследований, связанных с изучением роботов.

Учебное пособие

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

Приобретение данных

Платформы обычно предоставляют API для получения K-линий с данными истории, а некоторые также предоставляют данные о выполнении торговли по торговле.

Далее мы продемонстрируем, как получить и сохранить данные K-линии вечных контрактов на Binance.

Во-первых, найдите документацию на Бинанс:https://binance-docs.github.io/apidocs/futures/cn/#c59e471e81. Вы можете увидеть необходимые параметры и возвращенные форматы данных. Обычно количество K-линий, приобретенных API, ограничено, а Binance имеет максимум 1000, поэтому он должен быть приобретен путем итерации петли. Ситуация на других платформах аналогична Binance. Обратите внимание, что сеть должна быть подключена к зарубежной сети (по сравнению с внутренней сетью в Китае), чтобы сканировать K-линии.

Периоды поддержки Binance: 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M.

В [24]: запросы импорта #просьбы сети для общей библиотеки от даты времени даты ввоза, даты времени Время импорта Импорт панды как pd В [160]: def GetKlines ((символ=BTC,start=2020-8-10,end=2021-8-10,period=1h): Клин = [] start_time = int(time.mktime(datetime.strptime(start, %Y-%m-%d).timetuple))) *1000 end_time = int(time.mktime(datetime.strptime(end, %Y-%m-%d).timetuple))) *1000 в то время как время начала < время окончания: res = requests.get ((https://fapi.binance.com/fapi/v1/klines?symbol=%sUSDT&interval=%s&startTime=%s&limit=1000% ((symbol,period,start_time)) res_list = res.json() Klines += res_list #print ((datetime.utcfromtimestamp ((start_time/1000).strftime ((%Y-%m-%d %H:%M:%S), len ((res_list)) start_time = res_list[-1][0] return pd.DataFrame ((Klines,columns=[time,open,high,low,close,amount,end_time,volume,count,buy_amount,buy_volume,null]).astype)) В [85]: df = GetKlines ((символ=BTC,start=2021-1-1,end=2021-8-10,period=1h)

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

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

В [86]: df.to_csv ((btc_klines.csv) df = pd.read_csv ((btc_klines.csv,index_col=0) В [87]: df Выход[87]: ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,. Время открытия высокая низкая сумма закрытия конец_время количество покупать_количество покупать_объем нуль 0 1596988800000 11575.08 11642.00 11566.07 11591.37 6541.466 1596992399999 7.592336e+07 25724 3127.898 3.630633e+07 0 1 1596992400000 11591.39 11610.23 11526.90 11534.39 6969.252 1596995999999 8.057780e+07 27403 3390.424 3.920162e+07 0 2 1596996000000 11534.39 11656.69 11527.93 11641.07 6439.365 1596999599999 7.469135e+07 25403 3446.186 3.997906e+07 0 3 1596999600000 11641.06 11665.90 11624.20 11635.30 3911.582 1597003199999 4.555459e+07 17820 1842.413 2.145768e+07 0 4 1597003200000 11635.29 11684.00 11635.29 11673.81 3461.004 1597006799999 4.036804e+07 15513 1660.575 1.936981e+07 0 .......................................................................................................................................................................................................................................................................................................................................... 8805 1628658000000 45627.72 45894.53 45540.00 45801.45 10296.202 1628661599999 4.710187e+08 112187 4988.565 2.282399e+08 0 8806 1628661600000 45801.46 46270.00 45800.01 46087.86 26409.962 1628665199999 1.215164e+09 247170 13696.301 6.302708e+08 0 8807 1628665200000 46087.87 46450.00 46087.87 46367.38 23969.309 1628668799999 1.110210e+09 232348 11990.951 5.554267e+08 0 8808 1628668800000 46367.37 46643.13 46002.01 46217.01 23472.769 1628672399999 1.086549e+09 229533 12334.292 5.711837e+08 0 8809 1628672400000 46217.01 46329.69 46046.54 46297.16 6579.477 16286759999 3.039580e+08 78812 3313.055 1.530718e+08 0 ... 8810 строк × 12 столбцов

- Да. В [88]: df.index = pd.to_datetime ((df.time,unit=ms) #преобразовать индекс в дату, что удобно для графики В [89]: df.close.plot ((figsize=(15,6),grid = True); #close price Выход[89]:imgВ [92]: (df.buy_amount.rolling(150).mean()/df.amount.rolling(150.mean()).graph ((figsize=(15,6),grid = True); #после плоского, доля инициативной суммы покупки Ситуация, когда доля инициативной покупки увеличивается после достижения дна, обычно отвечает ситуации роста цен, но долгосрочный средний показатель доли инициативной покупки составляет 49%. Выход[92]:imgВ [93]: (df[count].rolling(100).mean (()).plot ((figsize=(15,6),grid = True); #исполненная сумма после плоской,и рыночные котировки могут быть подготовлены в низком месте Выход[93]:img

Мотор для обратного тестирования

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

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

Введение класса обмена:

  • account:USDT указывает базовую валюту, которая не требуется; realized_profit: уже реализованная прибыль и убыток; unrealised_profit: еще не реализованная прибыль и убыток; сумма: общий собственный капитал; комиссионная: комиссионная за обработку. Для других торговых пар сумма (которая является отрицательным числом при покупке); hold_price: цена хранения; стоимость: стоимость хранения; цена: текущая цена.

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

  • сбор: сбор за передачу; проще говоря, не различайте производителя и получателя.

  • initial_balance: первоначальные активы; первоначальная сумма двойной торговой пары по умолчанию равна 0.

  • Функция покупки: покупка, которая соответствует длинному и короткому закрытию постоянных контрактов без механизма соответствия.

  • Функция продажи: продавать.

  • Функция обновления: для обновления информации о счете, которая должна быть передана в словарь цен всех торговых пар. В [98]: класс Обмен:

    def Инит(self, trade_symbols, fee=0.0004, initial_balance=10000): self.initial_balance = initial_balance #первоначальный баланс self.fee = плата self.trade_symbols = trade_symbols self.account = {USDT:{realised_profit:0, unrealised_profit:0, total:initial_balance, fee:0}} для символа в trade_symbols: self.account[symbol] = {amount:0, hold_price:0, value:0, price:0, realised_profit:0,unrealised_profit:0,fee:0}

    def Торговля ((самостоятельно, символ, направление, цена, сумма):

      cover_amount = 0 if direction*self.account[symbol]['amount'] >=0 else min(abs(self.account[symbol]['amount']), amount)
      open_amount = amount - cover_amount
      self.account['USDT']['realised_profit'] -= price*amount*self.fee #take out the fee 
      self.account['USDT']['fee'] += price*amount*self.fee
      self.account[symbol]['fee'] += price*amount*self.fee
    
      if cover_amount > 0: #close first 
          self.account['USDT']['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount  #profit 
          self.account[symbol]['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount
          
          self.account[symbol]['amount'] -= -direction*cover_amount
          self.account[symbol]['hold_price'] = 0 if self.account[symbol]['amount'] == 0 else self.account[symbol]['hold_price']
          
      if open_amount > 0:
          total_cost = self.account[symbol]['hold_price']*direction*self.account[symbol]['amount'] + price*open_amount
          total_amount = direction*self.account[symbol]['amount']+open_amount
          
          self.account[symbol]['hold_price'] = total_cost/total_amount
          self.account[symbol]['amount'] += direction*open_amount
    

    def Купить ((самостоятельно, символ, цена, сумма):self.Trade(символ 1, цена, сумма)

    def Продать ((самостоятельно, символ, цена, сумма):self.Trade(символ -1, цена, сумма)

    def Update ((self, close_price): #обновление активов собственный счет[USDT][нереализованная_прибыль] = 0 для символа в self.trade_symbols: self.account[символ][unrealised_profit] = (close_price[символ] - self.account[символ][hold_price]) *self.account[символ][amount] self.account[символ][price] = close_price[символ] self.account[символ][value] = abs(self.account[символ][amount]) *close_price[символ] self.account[USDT][unrealised_profit] += self.account[символ][unrealised_profit] self.account[USDT][total] = круглый ((self.account[USDT][realised_profit] + self.initial_balance + self.account[USDT][unrealised_profit],6) В [117]: #В тесте вы можете видеть, что нет никакого акцента на том, является ли платформа маржинальной USDT или спотовой. e = Exchange([BTC], fee=0.0004, initial_balance=10000) #создать объект Exchange, и только одну торговую пару BTC e.Buy ((BTC,40000, 0.1) #buy 0.1 BTC по цене 40,000 e.Продать ((BTC,41000, 0.1) #продать 0,1 BTC по цене 41 000 e.Update (({BTC:41000}) #updtae информация о счете print ((e.account) #окончательная информация о счете print (("Прибыль: ',round ((e.account[USDT][total]-e.initial_balance,2)) Выход[117]:{USDT: {реализованная_прибыль: 96.76, нереализованная_прибыль: 0.0, общий: 10096.76, пошлина: 3.24}, BTC: {сумма: 0.0, держание_цены: 0, стоимость: 0.0, цена: 000, 41 реализованная_прибыль: 100.0, нереализованная_прибыль: 0.0, пошлина: 3.24}} Прибыль: 96,76

Стереотипная стратегия сетки

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

В верхней части Live есть официальный бот, начавшийся с 4 апреля 2021 года; значение позиции 150, расстояние между сетками - 0,01, а текущая прибыль - 3600USDT. Используя те же параметры и 5min K-линию для обратного тестирования, прибыль составляет 3937USDT. Поскольку значение позиции в начале бота менее 150 USDT, результат довольно точен. Если вы измените расстояние между сетками до 0,005, выигрыш будет 5226U. Расстояние между сетками 0,005 очевидно лучший параметр, чем 0,01, который необходимо проверить, чтобы выяснить.

Чем короче период K-линии, тем точнее соответствующие результаты бэкстеста, и тем больше требуемый объем данных.

В [241]: символ = TRX df = GetKlines ((символ=символ,start=2021-4-4,end=2021-8-11,period=5m) В [286]: значение = 150 pct = 0,01

e = Exchange (([символ], сбор=0.0002, первоначальный_баланс=10000) init_price = df.loc[0,close] res_list = [] # используется для хранения среднего результата для строки в df.iterrows(): kline = row[1] #что будет тестировать только одну K-линию и получать только один ордер на покупку или один ордер на продажу, что не очень точно buy_price = (value / pct - value) / ((value / pct) / init_price + e.account[symbol][amount]) #продажа цена заказа, поскольку это исполнение производителя, также является окончательной ценой соответствия sell_price = (value / pct + value) / ((value / pct) / init_price + e.account[символ][amount])

if kline.low < buy_price: #the lowest price of K-line is less than the current maker price; the buy order is executed 
    e.Buy(symbol,buy_price,value/buy_price)
if kline.high > sell_price:
    e.Sell(symbol,sell_price,value/sell_price)
e.Update({symbol:kline.close})
res_list.append([kline.time, kline.close, e.account[symbol]['amount'], e.account['USDT']['total']-e.initial_balance])

res = pd.DataFrame ((data=res_list, columns=[time,price,amount,profit]) res.index = pd.to_datetime ((res.time,unit=ms) В [287]: e.счет Выход[287]:{USDT: {реализованная_прибыль: 3866.633149565143, нереализованная_прибыль: 70.54622281993666, в общей сложности : 13937.179372, пошлина: 177,51000000000596}, TRX: {сумма: 36497.43208747655, держание цены: 0,08203709078461048, значение: 3064.689372385406, цена : 0,08397, реализованная_прибыль: 4044.143149565462, нереализованная_прибыль: 70.54622281993666, fee: 177.51000000000596}} В [288]: res.profit.plot ((figsize=(15,6),grid = True); Выход[288]:imgВ [170]: res.price.plot ((figsize=(15,6),grid = True); #close price Выход[170]:img

Спотная стратегия равновесия

Этот тип стратегии также относительно популярен, но платформа FMZ не очень хороша в обратном тестировании стратегий с несколькими символами, просто используйте этот механизм обратного тестирования, чтобы попробовать. Мы выбираем четыре основных валютных символа, BTC, ETH, LTC и XRP, и настраиваем 25% рыночной стоимости соответственно, и балансируем каждое 1% отклонение.

Во-первых, получите цены закрытия четырех символов за последний год. Можно увидеть, что ETH имеет наибольший рост, а другие три имеют аналогичные увеличения. Если вы держите эти четыре символа в среднем, конечная чистая стоимость составляет 4.5. После обратного тестирования стратегия равновесия имеет конечную чистая стоимость 5.3, которая немного улучшена.

В [290]: символы = [BTC,ETH,LTC,XRP] данные = {} для символа в символах: df = GetKlines ((символ=символ,start=2020-8-11,end=2021-8-11,period=1h) data[symbol] = df.close (данные[символ] = df.close) В [291]: df = pd.DataFrame (([данные[символ].значения символа в символах],index=символы).T В [302]: e = обменные символы, плата=0.0004, начальное_остаток=10000) res_list = [] для строки в df.iterrows(): цены = строка[1] сумма = e.account[USDT][total] e.Обновление цен) для символа в символах: pct = e.счет[символ][стоимость]/общий если pct > 0,26: e.Продажа ((символ,цены[символ],(пункт-0.25)*общий/цены[символ]) если pct < 0,24: e.Купить ((символ,цены[символ],(0,25%) *всего/цены[символ]) res_list.append (([e.account[symbol][value] для символа в символах] + [e.account[USDT][total]]) res = pd.DataFrame ((data=res_list, столбцы=символы+[total]) В [303]: (df/df.iloc[0,:]).plot(figsize=(15,6),grid = True); #plot the trand by normalization (Плотнообразование по нормализации) Выход[303]:imgВ [304]: (res.total/10000-(df/df.iloc[0,:]).mean(axis=1)).plot(figsize=(15,6),grid = True); #enheance эффект Выход[304]:img

Стратегия черепахи

Стратегия черепахи - это классическая стратегия тренда, которая включает в себя полную логику стоп-лосса для добавления позиций.https://zhuanlan.zhihu.com/p/27987938Мы будем реализовывать простую версию здесь для бэкстеста.

Период стратегии черепахи оказывает большое влияние на стратегию, и не рекомендуется выбирать слишком короткий период. Здесь мы выбираем 6h. Период канала Дончиана выбирается как 5, а отношение позиций выбирается как 0,003 в соответствии с бэкстером. Когда цена проходит через upBand канала для открытия 1 единицы длинной позиции, и цена продолжает расти на 0,3 волатильности после открытия позиций, продолжайте добавлять 1 единицу, и цена падает ниже 2,5 волатильности последней открытой цены для остановки убытка. Принцип короткого ордера тот же. Из-за большого бычьего рынка ETH стратегия черепахи захватила основной тренд и в конечном итоге достигла 27 раз прибыли, с максимальным рычагом использования 4 раза в течение периода.

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

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

В [424]: символ = ETH df = GetKlines ((символ=символ,start=2019-8-11,end=2021-8-11,period=6h) В [425]: df.index = pd.to_datetime ((df.time,unit=ms) В [568]: M = 5 # объем периода Дончианского канала pct = 0,003 #доля добавленных позиций в общем количестве позиций df[up] = df[high].rolling ((M).max().shift(1) #upBand Донкианского канала, используется для длинного прорыва t df[down] = df[low].rolling(M).max().shift(1) df[middle] = (df[up]+df[down])/2 df[true_range] = pd.concat([df[high]-df[low],df[high]-df[close].shift(1),df[close].shift(1)-df[low],ось=1).max (ось=1) df[N] = df[true_range].rolling(50).mean() #N равен недавней волатильности, используемой для суждения о покупке и остановке потерь В [572]: open_times = 0.3 #оценка открытия позиции Stop_times = 2,5 #stop loss e = Exchange([символ], fee=0.0004, initial_balance=10000) #установить получателя на 0,0004 res_list = [] last_price = 0 #конец открытой позиции для строки в df.iterrows(): клин = ряд[1] если kline.isnull().sum() > 0: #пропустите раздел без данных Продолжайте единицы = е.счет[USDT][всего]*pct/kline.N #размер единицы открытой позиции

if kline.high >  kline.up and e.account[symbol]['amount'] == 0: #first time to open long position 
    e.Buy(symbol,kline.up,unit) #notice the trading price here
    last_price = kline.up
if e.account[symbol]['amount'] > 0 and kline.high > last_price + open_times*kline.N: #long position, buy in 
    e.Buy(symbol,last_price + open_times*kline.N,unit)
    last_price = last_price + open_times*kline.N
if e.account[symbol]['amount'] > 0 and kline.low < last_price - stop_times*kline.N: #long position, stop loss
    e.Sell(symbol,last_price - stop_times*kline.N,e.account[symbol]['amount'])
    
if kline.low <  kline.down and e.account[symbol]['amount'] == 0: #open short
    e.Sell(symbol,kline.down,unit)
    last_price = kline.down
if e.account[symbol]['amount'] < 0 and kline.low < last_price - open_times*kline.N: #short position, buy in 
    e.Sell(symbol,last_price - open_times*kline.N,unit)
    last_price = last_price - open_times*kline.N
if e.account[symbol]['amount'] < 0 and kline.high > last_price + stop_times*kline.N: #short position, stop loss
    e.Buy(symbol,last_price + stop_times*kline.N,-e.account[symbol]['amount'])
    
e.Update({symbol:kline.close})
res_list.append([kline.time, kline.close, e.account[symbol]['amount']*kline.close, e.account['USDT']['total']])

res = pd.DataFrame ((data=res_list, columns=[time,price,value,total]) res.index = pd.to_datetime ((res.time,unit=ms) print(Конечная рыночная стоимость:,res[total][-1]) Выход[572]:Конечная рыночная стоимость: 280760.566996 В [573]: res.total.plot ((figsize=(15,6), сетка = True); Выход[573]:imgВ [571]: (res.value/res.total).plot ((figsize=(15,6), сетка = True); Выход[571]:img

Заключение

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

Используйте Python для анализа данных:https://wizardforcel.gitbooks.io/pyda-2e/content/

Количественное обучение Python:https://wizardforcel.gitbooks.io/python-quant-uqer/content/

В [ ]:


Больше