Начало было положено
тут
Привет смартлаб. Свою «карьеру» на СЛ, как и собственно карьеру в трейдинге я начал с цикла статей, как я «готовил» протокол FIX для валютного рынка. К слову сказать, тогда еще молодой провинциал никому не был нужен в столицах, я был прижат к стене и просто начал писать, как я готовлюсь к работе. Это сыграло сильнейшую роль в моем будущем.
Сейчас я не преследую никаких целей. Хотя… Тут не стоит зарекаться. Как сказал однажды коллега: «Ты конечно молодец, создал себе на СЛ портфолио и теперь оно играет на тебя». Ну да, что есть, то есть. Но на текущий момент, мне просто интересно рассказывать, как я иду по этим ступенькам. Слушать и прислушиваться к вашим комментам. Так что вы тут можете мене ругать, отрицать и отговаривать, как в предыдущем топике )
Собственно почему? Ну знаете, это профессиональная чуйка. Я стал видеть в них будущее. Это знаете, как примерно в 2017 почувствовался вкус денег в стратегиях и пришла такая же чуйка, что нужно срочно переквалифицироваться на fpga разработчика и начинать развивать эту тему в стратах. Чего уж там скрывать, это тогда сыграло решающую роль и с тех пор, скорее всего, я могу назвать себя уже неплохим спецом в этой тематике.
Вот также и сейчасc. Я полный ноль и чувствую молодежь начнет съедать меня, если не начну сейчас посвящать этой теме хотя бы 1-2 часа в двое суток.
Такс. Приступим
Спасибо вам всем за отклики в предыдущем топике. Ряд их мне очень помог. Что я понял? Что мне нужен какой то
десептикон персептрон. И я начал рыть.
Стало понятно, что все нейронки делятся на 3-4 типа решаемых задач. И моя задача, которая нужная чисто для изучения нейронок, относится к разряду классификации (ну например классифицировать тот или иной паттерн). А чтобы ее решить, нужен мне хотя бы однослойный персептрон.
Я много прочитал про нейронки. Про эти персептроны. И что я решил для себя? Пусть она для меня по прежнему остается черным ящиком пока. Я уверен, как всегда, все придет само, когда я буду к этому готов. Ну а пока, я запомню, что это будет черный ящик, на вход которого я подам какие то данные, от них возьмется какая то функция (я даже знаю какая), а от этой функции возьмется еще одна функция (типа выход), чтобы узнать конечный результат. Это все, что пока мне надо.
Далее, я тупо, по совету из прошлой темы, обратился к chatgpt, позадавал ей вопросы, которые не мог найти (типа «сколько надо делать слоев», «сколько надо делать нейронов») и тупо закончил это исследование «пример на питоне однослойного персептрона», «пример обучения на питоне однослойного персептрона»
Ну хорошо, на этом и порешаем, моя первая нейроночка будет выглядеть так:
class Perceptron:
def __init__ (self, _input_cnt):
self.weights = np.zeros (_input_cnt)
self.bias = 0
def predict (self, _inputs):
z = np.dot (_inputs, self.weights) + self.bias # сумматорная функция
return np.where (z > 0, 1, 0) # пороговая функция активации
def train (self, _inputs, _targets, _learning_rate = 0.1, _epochs = 100):
for epoch in range (_epochs):
for input, target in zip (_inputs, _targets):
prediction = self.predict (input)
error = target - prediction # ошибка предсказания
self.weights += _learning_rate * error * input # корректировка весов
self.bias += _learning_rate * error # корректировка смещения
Теперь данные
По мере того, как я все больше читал и изучал, я понял, что изначально задачу я поставил не правильно. Мне не нужно прогонять какой паттерн на истории, мне на истории нужно
выявить паттерн. Хотя бы чисто ради интереса.
Для этого я поставил задачу: выявить три свечи, где хай третьей больше первых двух и где последующие хаи пяти свечей буду всегда ниже это третье. Банально, я хотел выявить нечто похожее, правда сильно ошибался:
Так же, по мере углубления, я быстро понял, что не важно насколько круто написана нейронка, пока что важно ее грамотно и качественно обучить. При постановке такой задачи, считаю, что процесс обучения можно было бы легко автоматизировать, подсунув ей свечи любого инструмента.
Сами свечи я решил дернуть по привычке по iss, ну а потом подгружать их из файла, чтобы не часто дергать сервер moex:
class Data:
def __init__ (self, _sec_code):
self.sec_code = _sec_code
self.df = []
def get_cndls_moex (self):
with requests.Session() as session:
data = apimoex.get_market_candles (session, security = self.sec_code, interval=10, start=DATE_START, end=DATE_END, market='shares', engine='stock')
self.df = pd.DataFrame (data)
self.df['begin'] = pd.to_datetime (self.df ['begin'])
self.df.rename (columns = {'begin':'dtm'}, inplace = True)
def get_cndls_csv (self, _file_name):
self.df = pd.read_csv (_file_name, sep = '\t')
def save (self, _file_name):
self.df.to_csv (_file_name, sep = '\t', index = False)
Приведение данных
Что я решил подавать на вход этого черного ящика? Первые три свечи, типа паттерн. Ну так, чисто для обучения. Эти свечи нужно как то характеризовать, для этого я пока выбрал такие характеристики:
- размер тела
- размер верхнего хвоста
- размер нижнего хвоста
- полный размер свечи (хай — лоу)
при этом понятно, что эти величины нужно считать как относительные, я не прямые величины в пунктах (цены то везде разные) То есть относительно чего то, например отношение тела к размеру всей свечи или отношение нижнего хвоста к размеру всей свечи. Банально, если приглядеться, я распили свечь на три части: два хвоста и тело и высчитал их процентное отношение ко всему размеру.
Далее, инструменты у нас разные, ну или инструмент один, а рынок разный. И здесь нужно вводить относительностью. Рынок у нас обычно чем характеризируется? Ну вроде ликвидность и волатильность. Поэтому я решил привязаться к волатильности и ввести еще один показатель свечи, так как: отношение размера свечи к ATR, то есть
((high — low) / atr). Это чтобы наша нейроночка не сильно привязывалась к инструменту.
В питоне это выглядит примерно так:
#подсчет хвостов
def calc_tails (self):
self.df.loc [(self.df.close >= self.df.open),"range_h"] = self.df ['high'] - self.df ['close']
self.df.loc [(self.df.close < self.df.open),"range_h"] = self.df ['high'] - self.df ['open']
self.df.loc [(self.df.close >= self.df.open),"range_l"] = self.df ['open'] - self.df ['low']
self.df.loc [(self.df.close < self.df.open),"range_l"] = self.df ['close'] - self.df ['low']
#подсчет тела
def calc_range (self):
self.df ['range'] = self.df ['high'] - self.df ['low']
self.df ['range_b'] = self.df ['close'] - self.df ['open']
def calc_atr (self):
df_tr = pd.DataFrame([self.df ['high'] - self.df ['low'], abs (self.df ['high'] - self.df ['close'].shift()), abs (self.df ['low'] - self.df ['close'].shift())]).T.max (axis=1)
self.df ['atr'] = df_tr.rolling (window = 14).mean ().round (3)
#подсчет характеристик свечей
def calc_cndls_desc (self):
self.df ['desc_range'] = (self.df ['range'] / self.df ['atr']).round (2)
self.df ['desc_b'] = (self.df ['range_b'] / self.df ['range']).abs().round (2)
self.df ['desc_h'] = (self.df ['range_h'] / self.df ['range']).round (2)
self.df ['desc_l'] = (self.df ['range_l'] / self.df ['range']).round (2)
self.df ['desc_direct'] = 0
self.df.loc [(self.df.range_b >= 0),"desc_direct"] = 1
Ну и осталось разметить результат, для этого я ввел два признака:
- tag_1 — когда хаи последующих пяти свечей ниже текущей
- tag_2 — когда хаи предыдущих двух свечей ниже текущей
Питон конечно круто со своим пандасом работает как sql:
def set_tag_1 (self):
self.df ['tag_1'] = 0
self.df.loc [
(
(self.df['high'].diff (-1) > 0) &
(self.df['high'].diff (-2) > 0) &
(self.df['high'].diff (-3) > 0) &
(self.df['high'].diff (-4) > 0) &
(self.df['high'].diff (-5) > 0)
),
"tag_1"] = 1
def set_tag_2 (self):
self.df ['tag_2'] = 0
self.df.loc [
(
(self.df['high'].diff (1) > 0) &
(self.df['high'].diff (2) > 0)
),
"tag_2"] = 1
Получаем, когда tag_1 и tag_2 равны 1, это получается примерно тот паттерн, который будет перебираться на истории.
Мой первый FAIL
Вообще, что меня сразу начало тревожить, я думал, что если я задам такие характеристики свечей, я тупо не найду на истории хотя бы одну точно такую же свечь.
Но глянув стату с помощью:
def group (self):
gr_df = pd.DataFrame (self.df [['desc_b', 'desc_h', 'desc_l']])
gr_df = gr_df.groupby(['desc_b', 'desc_h', 'desc_l']).size ()
print (gr_df)
я быстро отмел эти сомнения, похожих свечей полно. Будут ли похожие паттерны? Ну посмотрим. На этот счет у меня будет запасной вариант, ну например, для характеристик свечей вводить допуск. То есть тело не 20%, а диапазон 20-30%. Вообщем практика покажет. Как видите я пишу прям online и будущего не знаю.
Но настоящий fail я получил, когда начал автоматически сохранять эти участки графика, чтобы просто глянуть, что там. Кстати питон все так же крут и делает это предельно легко:
def draw_targets_cndl (self, _targets):
clr_down= 'red'
clr_up = 'green'
width = .3
width2 = .03
for i in range (10):
idx = _targets.iloc [i:i+1].index.values[0]
df = pd.DataFrame (self.df.iloc [idx - 2 : idx + 6])
print ()
print (df)
up = df [df.close >= df.open]
down = df [df.close < df.open]
plt.figure()
plt.bar (up.index, up.close-up.open, width, bottom = up.open, color = clr_up)
plt.bar (up.index, up.high-up.close, width2, bottom = up.close, color = clr_up)
plt.bar (up.index, up.low-up.open, width2, bottom = up.open, color = clr_up)
plt.bar (down.index, down.close-down.open, width, bottom = down.open, color = clr_down)
plt.bar (down.index, down.high-down.open, width2, bottom = down.open, color = clr_down)
plt.bar (down.index, down.low-down.close, width2, bottom = down.close, color = clr_down)
plt.xticks (rotation = 30, ha = 'right')
plt.savefig("img/targets/" + str (i) + ".png")
И первая картинка мне прям зашла:
Но когда я стал перебирать далее, это просто FAIL. Давайте поржем вместе:
Кстати по началу я думал, что хаи третьей свечи тупо перебивают остальные, но тут какой то баг. Буду разбираться.
Не скрою, что есть прямо крутые картинки, но чего их смотреть, победителей на СЛ и так хватает.
Вообщем пока нравится мне эта задача своей простотой. Как видите, я еще не начинал обучение, о котором буду писать далее )
Всем удачи)
Интересно будет посмотреть, как минимум с какого-то этапа обучения), да и так уже любопытно.
Я бы только нейросети сразу писал, даже если простейшая архитектура (читай однослойный перцептрон), на каком-нибудь фреймворке, не знаю, какой поинтуитивней — keras, наверно. В ML всё на библиотеках, видишь как ты сдружился с pandas, нейросеть на голом питоне это примерно как таблицы голым питоном, а не пандасом обрабатывать). Ну эт так, для следующих шагов, когда поймёшь, что с однослойным перцептроном каши не сваришь)).
Удачи.
Наносекунды для МОЕХ бессмысленны.
У меня обычный и еще сервер брокера до биржи. Плюс еще тормозной Квик.
Тем не менее, у меня не ХФТ, и я не вижу для себя ухудшений работы от задержек в 1-2 с. Плюс копейки неинтересно.
P.S. для С++ программера самая большая сложность в Lua — в процессе писания кода абстрагироваться от того, насколько там все через жопу выполняется. Иначе грустные мысли и нервный тик в виде вредных микрооптимизаций :)
Да и всю ТС писать на Луа моветон.
это как, когда впервые сел изучать программирование GUI, типа Delphi и первая задача для примера hello world у всех на ум приходит калькулятор )
И тогда как раз результаты движений и проступают. Например, можно взять всеми любимые «уровни» и посчитать как глубоко от них стопосъём делают прежде чем пойти куда все ждут. А делают это по алгоритмам и объёмами, редко допускающими возражения.)
В целом сейчас эта задача, чтобы просто наработать опыт и компетенции, не более. Типа хэллоу ворлд
у Лехивана ничего выигрыша не дает, кроме инфоцыганства
А уже обученную можно вывести хоть в виде чистого C кода и куда угодно можно будет прошить.
Спасибо. Слежу за прогрессом.
Рекомендую попробовать подход, когда нейросеть предсказывает сигнал на вход. Покупать, ничего не делать, продавать.
1
0
-1
Активация сигмой
Фреймворки керас, тензорфлоу
Сети примитивные, как перевёрнутая пирамида Маслоу.
Решается задача классификации.
Выбросить идеи, что на вход нужно подавать свечи или другие ценовые данные.
На вход подавать сигналы: 1 или 0 (есть сигнал или нет сигнала).
Сигнал — это паттерн или индикатор.
Примеры сигналов:
— три восходящие свечи (close выше close)
— три нисходящие свечи (close ниже close)
— пересечение скользящих
— зоны перепроданности/перекупленности осцилляторов RSI и подобных
— пересечение кривых Stochastic в зонах перепроданности перекупленности
Нейросеть сама отсечёт левые сигналы
Чем больше сигналов подать на вход, тем лучше.
У меня результаты были до 90% точности по предсказаниям 2 из 3 классов — когда покупать, когда продавать.
Основная проблема — балансировка сети между классами.