Паттерны в трейдинге – примеры и картинки на графике. Эффективное применение в техническом анализе на криптовалютах и Форекс

Как искать паттерны в биржевых данных и использовать их в торговле?

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

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

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

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

Что такое паттерн и как его использовать?

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

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

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

Подготовка

  1. Для того, чтобы получать исторические данные и выставлять заявки на Forex через RESTv20 API, нам потребуется demo счет у известного брокера. Регистрация занимает минуту, после чего Вы получаете token (уникальный ключ для доступа) и номер счета.
  2. Необходим Python версии 2.7 с установленными библиотеками: oandapyV20, sklearn, matplotlib, numpy, psycopg2. Их можно установить через pip.
  3. Необходим PostgreSQL, у меня версия 9.6.

Описание модели

Самое первое, что нужно описать — собственно, исторические данные.

Создадим класс Candle, который будет хранить информацию о каждой свече:

class Candle: def __init__(self, datetime, ask, bid, volume): self.datetime = datetime self.ask = ask self.bid = bid self.volume = volume 

Описание паттерна будет таким:

class Pattern: result = '' serie = list() def __init__(self, serie, result): self.serie = serie self.result = result 

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

Введем еще два параметра:

  1. Длина серии (Length) — количество последовательных элементов в серии паттерна
  2. Ширина окна (window size) — количество последовательных элементов после серии, для хотя бы одного из которых выполняется условие выбора паттерна

Если мы покупаем по цене ask = X, то продать должны по возросшей цене bid > X. И наоборот, если мы продаем по цене bid = Y, то купить должны по цене ask < Y. В этом изменение цены будет больше спреда на момент покупки, и мы получим прибыль.
Сегодня я предлагаю использовать эти простые правила для отбора паттернов, но, на самом деле, чтобы все хорошо работало, к ним нужно добавить несколько фильтров. Это я предлагаю сделать Вам позже самостоятельно. Не забывайте, что выбор исходных данных (периода, рынка, инструмента и тп) очень важен — где-то паттерны есть, а где-то нет. Или нужно изменить условия их отбора.

Получаем данные

Получим данные от брокера и сохраним их в БД PostgreSQL. Первым делом, создадим класс, который будет загружать данные:

import pandas from oandapyV20.endpoints import instruments class StockDataDownloader(object): def get_data_from_finam(self, ticker, period, marketCode, insCode, dateFrom, dateTo): """Downloads data from FINAM.ru stock service""" addres = 'http://export.finam.ru/data.txt?market=' + str(marketCode) + '&em=' + str(insCode) + '&code=' + ticker + '&df=' + str(dateFrom.day) + '&mf=' + str(dateFrom.month-1) + '&yf=' + str(dateFrom.year) + '&dt=' + str(dateTo.day) + '&mt=' + str(dateTo.month-1) + '&yt=' + str(dateTo.year) + '&p=' + str(period + 2) + 'data&e=.txt&cn=GAZP&dtf=4&tmf=4&MSOR=1&sep=1&sep2=1&datf=5&at=1' return pandas.read_csv(addres) def get_data_from_oanda_fx(self, API, insName, timeFrame, dateFrom, dateTo): params = 'granularity=%s&from=%s&to=%s&price=BA' % (timeFrame, dateFrom.isoformat('T') + 'Z', dateTo.isoformat('T') + 'Z') r = instruments.InstrumentsCandles(insName, params=params) API.request(r) return r.response 

Бонус: я оставил в этом классе метод, который загружает любые исторические данные с Финама. Это очень удобно, потому что можно проанализировать как Forex, так и рынки ММВБ и ФОРТС. Минус только в том, что данные могут быть загружены с периодом не менее 1 минуты, в то время как второй метод может загрузить 5-секундные свечи.

Теперь сделаем простой скрипт, которые загружает данные в БД:

import psycopg2 from StockDataDownloader import StockDataDownloader from Conf import DbConfig, Config from datetime import datetime, timedelta import oandapyV20 import re step = 60*360 # download step, s daysTotal = 150 # download period, days dbConf = DbConfig.DbConfig() conf = Config.Config() connect = psycopg2.connect(database=dbConf.dbname, user=dbConf.user, host=dbConf.address, password=dbConf.password) cursor = connect.cursor() print 'Successfully connected' cursor.execute("SELECT * FROM pg_tables WHERE schemaname='public';") tables = list() for row in cursor: tables.append(row[1]) for name in tables: cmd = "DROP TABLE " + name print cmd cursor.execute(cmd) connect.commit() tName = conf.insName.lower() cmd = ('CREATE TABLE public."" ('  'datetimestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL,'  'ask FLOAT NOT NULL,'  'bid FLOAT NOT NULL,'  'volume FLOAT NOT NULL,'  'CONSTRAINT "PK_ID" PRIMARY KEY ("datetimestamp"));'  'CREATE UNIQUE INDEX timestamp_idx ON ("datetimestamp");').format(tName) cursor.execute(cmd) connect.commit() print 'Created table', tName downloader = StockDataDownloader.StockDataDownloader() oanda = oandapyV20.API(environment=conf.env, access_token=conf.token) def parse_date(ts): # parse date in UNIX time stamp return datetime.fromtimestamp(float(ts)) date = datetime.utcnow() - timedelta(days=daysTotal) dateStop = datetime.utcnow() candleDiff = conf.candleDiff if conf.candlePeriod == 'M': candleDiff = candleDiff * 60 if conf.candlePeriod == 'H': candleDiff = candleDiff * 3600 last_id = datetime.min while date < dateStop - timedelta(seconds=step): dateFrom = date dateTo = date + timedelta(seconds=step) data = downloader.get_data_from_oanda_fx(oanda, conf.insName, ''.format(conf.candlePeriod, conf.candleDiff), dateFrom, dateTo) if len(data.get('candles')) > 0: cmd = '' cmd = ('INSERT INTO VALUES').format(tName) cmd_bulk = '' for candle in data.get('candles'): volume = candle.get('volume') if volume != 0 and id!=last_id: cmd_bulk = cmd_bulk + ("(TIMESTAMP '',,,),n" .format(id, candle.get('ask')['c'], candle.get('bid')['c'], volume)) last_id = id if len(cmd_bulk) > 0: cmd = cmd + cmd_bulk[:-2] + ';' cursor.execute(cmd) connect.commit() print ("Saved candles from to ".format(dateFrom, dateTo)) date = dateTo cmd = "REINDEX INDEX timestamp_idx;" print cmd cursor.execute(cmd) connect.commit() connect.close() 

Если вы внимательно посмотрите на данные от Oanda, то увидите, что некоторые свечи пропущены. Причем, чем меньше период загружаемых данных, тем больше пропусков. Это не ошибка, а связано с тем, что цена за время пропусков не изменилась. Поэтому есть два способа загрузки таких данных — сохранять как есть, или добавлять пропущенные свечи со значениями, аналогичными последней свече от брокера с нулевым объемом. В репозитории на Github реализованы оба варианта, последний закомментирован. Так же, если Вы посчитаете нужным добавлять пропущенные свечи, есть скрипт DbCheck.py, проверяющий правильность последовательности свечей для этого случая.

Читать статью  Безапелляционное падение. Что происходит с рублем?

Анализ данных

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

import psycopg2 from Conf import DbConfig, Config from Desc.Candle import Candle from Desc.Pattern import Pattern import numpy def get_patterns_for_window_and_num(window, length, limit=None): conf = Config.Config() dbConf = DbConfig.DbConfig() connect = psycopg2.connect(database=dbConf.dbname, user=dbConf.user, host=dbConf.address, password=dbConf.password) cursor = connect.cursor() print 'Successfully connected' tName = conf.insName.lower() cmd = 'SELECT COUNT(*) FROM ;'.format(tName) cursor.execute(cmd) totalCount = cursor.fetchone()[0] print 'Total items count '.format(totalCount) cmd = 'SELECT * FROM ORDER BY datetimestamp'.format(tName) if limit is None: cmd = ';'.format(cmd) else: cmd = ' LIMIT ;'.format(cmd, limit) cursor.execute(cmd) wl = list() patterns = list() profits = list() indicies = list() i = 1 for row in cursor: nextCandle = Candle(row[0], row[1], row[2], row[3]) wl.append(nextCandle) print 'Row of , % total'.format(i, totalCount, 100*(float(i)/float(totalCount))) if len(wl) == window+length: # find pattern of 0..length elements # that indicates price falls / grows # in the next window elements to get profit candle = wl[length-1] ind = length + 1 # take real data only if candle.volume != 0: while ind candle.ask: # buy pattern p = Pattern(wl[:length],'buy') patterns.append(p) indicies.append(ind - length) profits.append(iCandle.bid - candle.ask) break if iCandle.ask < candle.bid: # sell pattern p = Pattern(wl[:length],'sell') patterns.append(p) indicies.append(ind - length) profits.append(candle.bid - iCandle.ask) break ind = ind + 1 wl.pop(0) i = i + 1 print 'Total patterns: '.format(len(patterns)) print 'Mean index[after]: '.format(numpy.mean(indicies)) print 'Mean profit: '.format(numpy.mean(profits)) connect.close() return patterns def pattern_serie_to_vector(pattern): sum = 0 for candle in pattern.serie: sum = sum + float(candle.ask + candle.bid) / 2; mean = sum / len(pattern.serie) vec = [] for candle in pattern.serie: vec = numpy.hstack((vec, [ (candle.ask+candle.bid) / (2 * mean) ])) return vec def get_x_y_for_patterns(patterns, expected_result): X = [] y = [] for p in patterns: X.append(pattern_serie_to_vector(p)) if (p.result == expected_result): y.append(1) else: y.append(0) return X, y 

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

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

Напоминаю, что площадь под ROC кривой меняет свое значение от 0.5 (самый плохой классификатор) до 1 (самый лучший классификатор). Наша цель — получить хотя бы 0.8.

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

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

# gradient boosting import numpy as np import matplotlib.pyplot as plt from sklearn.model_selection import KFold, cross_val_score from sklearn.ensemble import GradientBoostingClassifier from PatternsCollector import get_patterns_for_window_and_num, get_x_y_for_patterns import seaborn nums = [2,5,10] i = 0 wrange = [1,5,10] lrange = [5,10] values = list() legends = list() for wnd in wrange: for l in lrange: scores = [] patterns = get_patterns_for_window_and_num(wnd, l) X, y = get_x_y_for_patterns(patterns, 'buy') for n in nums: i = i+1 kf = KFold(n_splits=5, shuffle=True, random_state=100) model = GradientBoostingClassifier(n_estimators=n, random_state=100) ms = cross_val_score(model, X, y, cv=kf, scoring='roc_auc') scores.append(np.mean(ms)) print 'Calculated -, num=, %'.format(wnd, l, n, 100 * i/float((len(nums)*len(wrange)*len(lrange)))) values.append(scores) legends.append('-'.format(wnd, l)) plt.xlabel('estimators count') plt.ylabel('accuracy') for v in values: plt.plot(nums, v) plt.legend(legends) plt.show() 

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

# logistic regression from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler from PatternsCollector import get_patterns_for_window_and_num, get_x_y_for_patterns from sklearn.model_selection import KFold, cross_val_score import numpy as np import matplotlib.pyplot as plt import seaborn cr = [10.0 ** i for i in range(-3, 1)] i = 0 wrange = [1,5,10] lrange = [5, 10] values = list() legends = list() for wnd in wrange: for l in lrange: scores = [] patterns = get_patterns_for_window_and_num(wnd, l) X, y = get_x_y_for_patterns(patterns, 'buy') sc = StandardScaler() X_sc = sc.fit_transform(X) for c in cr: i = i+1 kf = KFold(n_splits=5, shuffle=True, random_state=100) model = LogisticRegression(C=c, random_state=100) ms = cross_val_score(model, X_sc, y, cv=kf, scoring='roc_auc') scores.append(np.mean(ms)) print 'Calculated -, C=, %'.format(wnd, l, c, 100 * i/float((len(cr)*len(wrange)*len(lrange)))) values.append(scores) legends.append('-'.format(wnd, l)) plt.xlabel('C value') plt.ylabel('accuracy') for v in values: plt.plot(cr, v) plt.legend(legends) plt.show() 

Как я уже говорил, условия неполные. Поэтому получаем точность всего 0.52. Но если вы их дополните, то точность будет лучше. Можете попробовать другие алгоритмы — нейросети, random forest и многие другие. Нужно не забыть про проблему переобучения — например, при большом числе деревьев в градиентном бустинге.

Проверка на ошибки в коде: если вместо реальных данных в БД взять от них sin(), то для обоих классификаторов AUC_ROC на кроссвалидации будет 0.96.

Торговый робот

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

import datetime from datetime import datetime from os import path import matplotlib.pyplot as plt import oandapyV20 import oandapyV20.endpoints.orders as orders import oandapyV20.endpoints.positions as positions from oandapyV20.contrib.requests import MarketOrderRequest from oandapyV20.contrib.requests import TakeProfitDetails, StopLossDetails from oandapyV20.endpoints.accounts import AccountDetails from oandapyV20.endpoints.pricing import PricingInfo from Conf.Config import Config import seaborn config = Config() oanda = oandapyV20.API(environment=config.env, access_token = config.token) pReq = PricingInfo(config.account_id, 'instruments='+config.insName) asks = list() bids = list() long_time = datetime.now() short_time = datetime.now() if config.write_back_log: f_back_log = open(path.relpath(config.back_log_path + '/' + config.insName + '_' + datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))+'.log', 'a'); time = 0 times = list() last_ask = 0 last_bid = 0 if config.write_back_log: print 'Backlog file name:', f_back_log.name f_back_log.write('DateTime,Instrument,ASK,BID,Price change,Status, Spread, Result n') def process_data(ask, bid, status): global last_result global last_ask global last_bid global long_time global short_time if status != 'tradeable': print config.insName, 'is halted.' return asks.append(ask) bids.append(bid) times.append(time) # --- begin strategy here --- # --- end strategy here --- if len(asks) > config.maxLength: asks.pop(0) if len(bids) > config.maxLength: bids.pop(0) if len(times) > config.maxLength: times.pop(0) if config.write_back_log: f_back_log.write('%s,%s,%s,%s,%s,%s,%s n' % (datetime.datetime.now(), config.insName, pReq.response.get('prices')[0].get('asks')[1].get('price'), pReq.response.get('prices')[0].get('bids')[1].get('price'), pChange, ask-bid, result)) def do_long(ask): if config.take_profit_value!=0 or config.stop_loss_value!=0: order = MarketOrderRequest(instrument=config.insName, units=config.lot_size, takeProfitOnFill=TakeProfitDetails(price=ask+config.take_profit_value).data, stopLossOnFill=StopLossDetails(price=ask-config.stop_loss_value).data) else: order = MarketOrderRequest(instrument=config.insName, units=config.lot_size) r = orders.OrderCreate(config.account_id, data=order.data) resp = oanda.request(r) print resp price = resp.get('orderFillTransaction').get('price') print time, 's: BUY price =', price return float(price) def do_short(bid): if config.take_profit_value!=0 or config.stop_loss_value!=0: order = MarketOrderRequest(instrument=config.insName, units=-config.lot_size, takeProfitOnFill=TakeProfitDetails(price=bid+config.take_profit_value).data, stopLossOnFill=StopLossDetails(price=bid-config.stop_loss_value).data) else: order = MarketOrderRequest(instrument=config.insName, units=-config.lot_size) r = orders.OrderCreate(config.account_id, data=order.data) resp = oanda.request(r) print resp price = resp.get('orderFillTransaction').get('price') print time, 's: SELL price =', price return float(price) def do_close_long(): try: r = positions.PositionClose(config.account_id, 'EUR_USD', ) resp = oanda.request(r) print resp pl = resp.get('longOrderFillTransaction').get('pl') real_profits.append(float(pl)) print time, 's: Closed. Profit = ', pl, ' price = ', resp.get('longOrderFillTransaction').get('price') except: print 'No long units to close' def do_close_short(): try: r = positions.PositionClose(config.account_id, 'EUR_USD', ) resp = oanda.request(r) print resp pl = resp.get('shortOrderFillTransaction').get('tradesClosed')[0].get('realizedPL') real_profits.append(float(pl)) print time, 's: Closed. Profit = ', pl, ' price = ', resp.get('shortOrderFillTransaction').get('price') except: print 'No short units to close' def get_bal(): r = AccountDetails(config.account_id) return oanda.request(r).get('account').get('balance') plt.ion() plt.grid(True) do_close_long() do_close_short() real_profits = list() while True: try: oanda.request(pReq) ask = float(pReq.response.get('prices')[0].get('asks')[0].get('price')) bid = float(pReq.response.get('prices')[0].get('bids')[0].get('price')) status = pReq.response.get('prices')[0].get('status') process_data(ask, bid, status) plt.clf() plt.subplot(1,2,1) plt.plot(times, asks, color='red', label='ASK') plt.plot(times, bids, color='blue', label='BID') if last_ask!=0: plt.axhline(last_ask, linestyle=':', color='red', label='curr ASK') if last_bid!=0: plt.axhline(last_bid, linestyle=':', color='blue', label='curr BID') plt.xlabel('Time, s') plt.ylabel('Price change') plt.legend(loc='upper left') plt.subplot(1, 2, 2) plt.hist(real_profits, label='Profits') plt.legend(loc='upper left') plt.xlabel('Profits') plt.ylabel('Counts') plt.tight_layout() except Exception as e: print e plt.pause(config.period) time = time + config.period 

Полный исходный код здесь.

Читать статью  Европейская торговая сессия – Часы работы, биржи и активы

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

Отдельное спасибо хочу сказать авторам курса от Яндекс по машинному обучению на Coursera. А так же Andrew Ng за замечательные лекции на этом же ресурсе.

UPDATE:
А вот что получается на градиентном бустинге по склееному фьючерсу SI с финама за последний год (если под критерием выбора паттерна понимать скачок цены на 1% в нужную сторону):

И это уже неплохой результат. Матожидание в плюс.
А тут как раз Альфа Директ выпустил новый серверный API 🙂

Паттерны в трейдинге – примеры и картинки на графике. Эффективное применение в техническом анализе на криптовалютах и Форекс

паттерны в трейдинге

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

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

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

Структура графика цены

Мы будем рассматривать именно те паттерны, которые образуются движением цены и образуют определенную зигзагообразную структуру графика. Есть еще свечные паттерны, но они изучены вдоль и поперек и с ними все более-менее ясно. Чего не скажешь про пресловутые «голову и плечи», «двойную вершину» и т.п.

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

Идеальная структура графикаРеальный график

Пример желтенького и красненького на рис. 1., а серенького и грязненького – на рис. 2. Разница налицо, как говорится. И еще нигде я не встречал нормального объяснения, как рассмотреть в хаосе черточек эти красивые паттерны из учебника. Причем на самом деле задача еще сложнее – нам надо не только научиться их находить визуально, а еще и программировать в виде кода робота.

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

Метод свечных диапазонов

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

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

Размер ценового диапазона рассчитывается по телам свечей, т.е. от уровня OPEN первой свечи до уровня CLOSE последней свечи диапазона. Диапазон засчитывается, если его размер больше либо равен Nпр. Величина Nпр зависит от актива и определяется анализом истории графика.

Итак, вот алгоритм метода свечных диапазонов.

1. Идем от левого края графика, ищем диапазоны.

2. Найденные диапазоны помечаем (рис. 3, белые линии).

Свечные диапазоны

3. Находим точки «перелома» (рис. 4):

А) после растущего диапазона идет падающий

Б) после падающего диапазона идет растущий

Точки перелома

4. В области точки А находим самый высокий HIGH – это будет верх зигзага

5. В области точки Б находим самый низкий LOW – это будет низ зигзага (рис. 5)

Зигзаг

6. Рисуем линиями структуру графика. После этого можно его сжать по времени для наглядности (рис. 6)

Сжатый график

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

Практические замечания

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

Определение экстремума

В алгоритме есть одно исключение – если цена рисует «ежа», т.е. диапазон образуется мелкими свечами с гигантскими тенями (рис. 8), то он не засчитывается и структуру не образует. В этом случае доминирующее движение цены не понятно, а непонятные ситуации лучше пропускать.

Исключение из правил

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

Читать статью  Как купить акции: для чего, сколько нужно денег и где их хранить

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

Особенности применения метода

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

Продолжим) В методе свечных диапазонов принципиальным вопросом является выбор значения Nпр. Тут дело вот в чем. Есть два вида движения цены – безоткатное и зигзагообразное (рис. 9). Разница между ними в том, что безоткатное движение имеет склонность к откату, т.е. развороту (А), а зигзагообразное – к продолжению движения в ту же сторону (Б).

Два типа движения

При этом структура графика, получаемая с помощью метода свечных диапазонов, сильно зависит от Nпр (рис. 10). Видно, что при Nпр = 10 п. движение выглядит безоткатным, готовящимся к развороту, а при Nпр = 8 п. — зигзагообразным, склонным к продолжению растущего тренда. Этот момент необходимо учитывать и периодически проверять различные значения Nпр на текущем графике.

Типы движения цены на графике

Еще один практический совет – при выборе Nпр лучше ошибиться в большую сторону, чем в меньшую. Так будет меньше «ложных срабатываний» — паттернов, которых на самом деле нет. Следовательно, меньше суеты и волнения, которые ведут к тильту и хаотичным сделкам. А нам этого не надо, мы хотим торговать спокойно и уверенно.

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

Реальные паттерны

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

В принципе, все, чтобы зарабатывать, у вас уже есть. Если вы читали любой учебник по трейдингу, вы уже все знаете. Только раньше вы не умели это применять. Давайте рассмотрим, как движется цена. Предположим, цена начинает безоткатно расти (рис. 11, А). Мы знаем, что безоткатное движение имеет склонность к развороту, поэтому на участке А нужно рассматривать только продажи. Далее цена делает откат (рис. 11, Б).

Тренд

Если предположить, что тренд восходящий, то цена должна обновить максимум. Соответственно на участке Б необходимо покупать. И наконец, в соответствии с теорией Чарльза Доу тренд имеет большую склонность к продолжению, чем к развороту. Следовательно, если предположить, что тренд будет продолжаться, то после пробоя ценой максимума необходимо покупать (рис. 11, В). Если же вы не верите Доу и предпочитаете торговать в контртренд, т.е. ожидаете разворота тренда, то на участке В надо продавать.

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

В какую сторону входить в сделку

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

Рассмотрим еще один пример структуры рынка. Цена не пролетает максимум, а отбивается и начинает «стучать» в него. В теории это называется консолидация, тестирование уровня и т.п. Здесь важный момент, что максимумы находятся примерно на одном уровне, совпадающем с предыдущим «большим» максимумом (рис. 12). При этом минимумы на разных уровнях.

Точка входа на покупку

Как мы видим, такая картинка очень удобна для входа в продажу, т.к. есть четкий уровень, который еще и подтверждается следующими максимумами. И неудобна для входа в покупку – непонятно, в каком месте входить, минимумы размыты. Соответственно толпа делает то, что удобно – продает, и ее благополучно выносят (рис. 13).

Вынос продавцов

Бывает обратная ситуация – минимумы четкие, а максимумы размытые (рис. 14). В этом случае вы уже знаете, что делать. Делать нужно то, что неудобно, что противоречит логике, что неестественно. В данном случае – продавать (рис. 15). Как конкретно использовать рассмотренные паттерны – это уже тема для вашего творчества. Главное, что вы знаете доминирующее движение цены.

Точка входа 2Вынос покупателей

Мой любимый паттерн

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

И в заключение рассмотрим паттерн, который известный трейдер Г.Задоя называет «самым ужасным» – это расширяющийся треугольник (рис. 16). Он представляет собой последовательность: максимум – минимум – ложный пробой максимума – ложный пробой минимума и т.д. Сколько раз цена вынесет минимум и максимум, заранее никогда не известно.

Расходящийся треугольник

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

Интересно то, что об этом почти никто не говорит. Паттерн «расширяющийся треугольник» в Интернете и в литературе можно встретить крайне редко, я такую картинку видел всего один раз. А между тем сейчас на рынке он встречается постоянно. Это говорит о том, что:

  • Информация в общеизвестных источниках часто неверная, устаревшая или намеренно вводящая в заблуждение
  • Рынок постоянно «учится» и «умнеет»

Паттерн «расширяющийся треугольник» я использую, например, в стратегии «Большая свеча», описанной в этой статье. Например, рис. 8 там – это не что иное, как расширяющийся треугольник и метод его использования. Наверняка вы тоже найдете хорошие способы применения этого паттерна и разработаете прибыльную торговую систему на их основе.

Резюме 10

Также рекомендую ознакомиться с моей мини-книгой «Финансовый ледокол»

Источник https://habr.com/ru/articles/324244/

Источник https://maxxen.ru/patterny-v-trejdinge/

Добавить комментарий

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