WLM1ke / poptimizer

Оптимизация долгосрочного портфеля акций
The Unlicense
153 stars 28 forks source link

Верятно зацикливается на одной Delta #106

Closed Snest1 closed 2 years ago

Snest1 commented 2 years ago

Добрый день, Михаил.

  1. После коммита https://github.com/WLM1ke/poptimizer/commit/df89e13145e66e62cc71265833f8cf0969206678 второй раз столкнулся с ситуацией, когда эволюция будто бы зацикливается. Нового обучения не происходит, параметр Delta не меняется.

Первый раз прервал эволюцию на 4ххх шаге, подумал что баг. После повторного ситуация повторилась. Посмотрите пожалуйста логи https://drive.google.com/drive/folders/1aonlwT0NKgGX2yuU4uYijlHzqFtCx1YB?usp=sharing , может быть я зря панику поднял. Можно поискать в файлах по слову "Delta" - там всегда "28".

  1. По этим логам не видите ли каких-то проблем? Довольно долго уже работает эволюция на хорошем железе, но hmean-оптимизация не предлагает ничего по изменению портфеля, а resample часто значительно меняет предложения.

Может быть можно с Вами онлайн проконсультироваться по конкретной инсталляции poptimizer, например за оплату :) ?

WLM1ke commented 2 years ago
  1. У вас там достаточно много организмов, эта ситуация штатная. Как вариант просто запускать каждый день, чтобы все пробежало на новых данных. Как популяция уменьшится, должно начать эволюционировать дальше
  2. Про hmean ничего не могу сказать - это патч от одного из пользователей. Про resample не до конца понимаю, что значит "часто значительно меняет предложения". Я без проблем могу проконсультировать - найдите меня в телеграмме WLMike -  почти всегда свободен. Можно хоть сейчас
Snest1 commented 2 years ago

Михаил, спасибо огромное!

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

Выяснили, что: 1) 59 бумаг, по которым Train Size~=89000, не достаточно для используеого метода машинного обучения. Вероятный минимум Train Size - 100к, лучше 200к+ (~200 бумаг), в идеале 800к+ 2) для обучения вполне нормально использовать гораздо бОльшее кол-во бумаг, даже если 3//4 из них не будут покупаться в портфель. 3) p-value используется не только во время оптимизации портфеля, но и при обучении. Лучше его не трогать, если не понимаешь для чего. 4) на многопроцессорных/многоядерных компьютерах увеличение num_workers в файле data_loader.py совсем незначительно (незаметно) поднимает скорость прохождения Tests во время процесса эволюции . На этом этапе гораздо больше времени тратится на чтение/передачу больших объемов данных, которое идет в один поток. Предположительно тут есть возможность для оптимизации кода, которая может дать выигрыш в скорости эволюции на порядок для системы с 16core-CPU. 5) использование GPU "в лоб" вероятно не принесет значительного прироста производительности во время эволюции.

vSINKS commented 2 years ago

Столкнулся с такой же ситуацией. Организмов - 143 / Максимум оценок - 142. Часто выходит сообщение "Удаляю - Слишком большая длинна истории - 159" или "Медленный, не размножается ...". Уже неделю работает в таком режиме. Я не совсем понял рекомендацию "...Как вариант просто запускать каждый день, чтобы все пробежало на новых данных. Как популяция уменьшится, должно начать эволюционировать дальше". Необходимо каждый день прерывать эволюцию и запускать снова? У меня популяция не уменьшилась, наоборот, приросла на 3-4 организма за неделю.

WLM1ke commented 2 years ago

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

PS: постараюсь на следующей неделе выкатить версию, где этой проблемы нет.

vSINKS commented 2 years ago

Спасибо!

Snest1 commented 2 years ago

Михаил, добрый день.

После обновлений от 3 июня три модели смогли обновится (у них изменилось кол-во tickers), часть удалились, и вторые сутки больше ничего не меняется. Всегда получаем "Слишком большая длинна истории", что сподвигло смотреть код.

Подскажите пожалуйста, нет ли ошибки в models.py?

... n_tickers = len(self._tickers) days, rez = divmod(len(loader.dataset), n_tickers) if rez: history = int(self._phenotype["data"]["history_days"]) raise TooLongHistoryError(f"Слишком большая длинна истории - {history}") ...

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

Я для отладки исправил вывод raise raise TooLongHistoryError(f"Слишком большая длинна истории - {history}; {days}, {rez} = divmod({len_loader_dataset}, {n_tickers})") и получил:

2022-06-05 09:46 Удаляю - Слишком большая длинна истории - 357; 0, 159 = divmod(159, 227) 2022-06-05 09:46 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:46 Удаляю - Слишком большая длинна истории - 222; 0, 226 = divmod(226, 227) 2022-06-05 09:46 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:46 Удаляю - Слишком большая длинна истории - 249; 0, 226 = divmod(226, 227) 2022-06-05 09:47 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:47 Удаляю - Слишком большая длинна истории - 254; 0, 225 = divmod(225, 227) 2022-06-05 09:47 Удаляю - Слишком большая длинна истории - 217; 0, 226 = divmod(226, 227) 2022-06-05 09:47 Удаляю - Слишком большая длинна истории - 243; 0, 226 = divmod(226, 227) 2022-06-05 09:47 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:47 Удаляю - Слишком большая длинна истории - 221; 0, 226 = divmod(226, 227) 2022-06-05 09:47 Удаляю - Слишком большая длинна истории - 250; 0, 225 = divmod(225, 227) 2022-06-05 09:47 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:47 Удаляю - Слишком большая длинна истории - 243; 0, 226 = divmod(226, 227) 2022-06-05 09:47 Удаляю - Слишком большая длинна истории - 218; 0, 226 = divmod(226, 227) 2022-06-05 09:48 Удаляю - Слишком большая длинна истории - 246; 0, 226 = divmod(226, 227) 2022-06-05 09:48 Удаляю - Слишком большая длинна истории - 249; 0, 226 = divmod(226, 227) 2022-06-05 09:48 Удаляю - Слишком большая длинна истории - 236; 0, 226 = divmod(226, 227) 2022-06-05 09:48 Удаляю - Слишком большая длинна истории - 247; 0, 226 = divmod(226, 227) 2022-06-05 09:48 Удаляю - Слишком большая длинна истории - 244; 0, 226 = divmod(226, 227) 2022-06-05 09:48 Удаляю - Слишком большая длинна истории - 243; 0, 226 = divmod(226, 227) 2022-06-05 09:48 Удаляю - Слишком большая длинна истории - 247; 0, 226 = divmod(226, 227) 2022-06-05 09:48 Удаляю - Слишком большая длинна истории - 242; 0, 226 = divmod(226, 227) 2022-06-05 09:48 Удаляю - Слишком большая длинна истории - 249; 0, 226 = divmod(226, 227) 2022-06-05 09:49 Удаляю - Слишком большая длинна истории - 214; 0, 226 = divmod(226, 227) 2022-06-05 09:49 Удаляю - Слишком большая длинна истории - 237; 0, 226 = divmod(226, 227) 2022-06-05 09:49 Удаляю - Слишком большая длинна истории - 248; 0, 226 = divmod(226, 227) 2022-06-05 09:49 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:49 Удаляю - Слишком большая длинна истории - 242; 0, 226 = divmod(226, 227) 2022-06-05 09:49 Удаляю - Слишком большая длинна истории - 230; 0, 226 = divmod(226, 227) 2022-06-05 09:49 Удаляю - Слишком большая длинна истории - 242; 0, 226 = divmod(226, 227) 2022-06-05 09:49 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:49 Удаляю - Слишком большая длинна истории - 217; 0, 226 = divmod(226, 227) 2022-06-05 09:49 Удаляю - Слишком большая длинна истории - 244; 0, 226 = divmod(226, 227) 2022-06-05 09:50 Удаляю - Слишком большая длинна истории - 231; 0, 226 = divmod(226, 227) 2022-06-05 09:50 Удаляю - Слишком большая длинна истории - 241; 0, 226 = divmod(226, 227) 2022-06-05 09:50 Удаляю - Слишком большая длинна истории - 218; 0, 226 = divmod(226, 227) 2022-06-05 09:50 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:50 Удаляю - Слишком большая длинна истории - 212; 0, 226 = divmod(226, 227) 2022-06-05 09:50 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:50 Удаляю - Слишком большая длинна истории - 246; 0, 226 = divmod(226, 227) 2022-06-05 09:50 Удаляю - Слишком большая длинна истории - 241; 0, 226 = divmod(226, 227) 2022-06-05 09:50 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:51 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:51 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:51 Удаляю - Слишком большая длинна истории - 250; 0, 225 = divmod(225, 227) 2022-06-05 09:51 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:51 Удаляю - Слишком большая длинна истории - 256; 0, 225 = divmod(225, 227) 2022-06-05 09:51 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:51 Удаляю - Слишком большая длинна истории - 248; 0, 226 = divmod(226, 227) 2022-06-05 09:51 Удаляю - Слишком большая длинна истории - 213; 0, 226 = divmod(226, 227) 2022-06-05 09:51 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:51 Удаляю - Слишком большая длинна истории - 250; 0, 225 = divmod(225, 227) 2022-06-05 09:52 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:52 Удаляю - Слишком большая длинна истории - 241; 0, 226 = divmod(226, 227) 2022-06-05 09:52 Удаляю - Слишком большая длинна истории - 215; 0, 226 = divmod(226, 227) 2022-06-05 09:52 Удаляю - Слишком большая длинна истории - 260; 0, 225 = divmod(225, 227) 2022-06-05 09:52 Удаляю - Слишком большая длинна истории - 250; 0, 225 = divmod(225, 227) 2022-06-05 09:52 Удаляю - Слишком большая длинна истории - 248; 0, 226 = divmod(226, 227) 2022-06-05 09:52 Удаляю - Слишком большая длинна истории - 241; 0, 226 = divmod(226, 227) 2022-06-05 09:52 Удаляю - Слишком большая длинна истории - 215; 0, 226 = divmod(226, 227) 2022-06-05 09:52 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:53 Удаляю - Слишком большая длинна истории - 247; 0, 226 = divmod(226, 227) 2022-06-05 09:53 Удаляю - Слишком большая длинна истории - 248; 0, 226 = divmod(226, 227) 2022-06-05 09:53 Удаляю - Слишком большая длинна истории - 239; 0, 226 = divmod(226, 227) 2022-06-05 09:53 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:53 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:53 Удаляю - Слишком большая длинна истории - 248; 0, 226 = divmod(226, 227) 2022-06-05 09:53 Удаляю - Слишком большая длинна истории - 255; 0, 225 = divmod(225, 227) 2022-06-05 09:53 Удаляю - Слишком большая длинна истории - 234; 0, 226 = divmod(226, 227) 2022-06-05 09:53 Удаляю - Слишком большая длинна истории - 250; 0, 225 = divmod(225, 227) 2022-06-05 09:53 Удаляю - Слишком большая длинна истории - 252; 0, 225 = divmod(225, 227) 2022-06-05 09:54 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:54 Удаляю - Слишком большая длинна истории - 261; 0, 225 = divmod(225, 227) 2022-06-05 09:54 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:54 Удаляю - Слишком большая длинна истории - 244; 0, 226 = divmod(226, 227) 2022-06-05 09:54 Удаляю - Слишком большая длинна истории - 242; 0, 226 = divmod(226, 227) 2022-06-05 09:54 Удаляю - Слишком большая длинна истории - 246; 0, 226 = divmod(226, 227) 2022-06-05 09:54 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:54 Удаляю - Слишком большая длинна истории - 243; 0, 226 = divmod(226, 227) 2022-06-05 09:54 Удаляю - Слишком большая длинна истории - 250; 0, 225 = divmod(225, 227) 2022-06-05 09:54 Удаляю - Слишком большая длинна истории - 250; 0, 225 = divmod(225, 227) 2022-06-05 09:55 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:55 Удаляю - Слишком большая длинна истории - 247; 0, 226 = divmod(226, 227) 2022-06-05 09:55 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:55 Удаляю - Слишком большая длинна истории - 214; 0, 226 = divmod(226, 227) 2022-06-05 09:55 Удаляю - Слишком большая длинна истории - 243; 0, 226 = divmod(226, 227) 2022-06-05 09:55 Удаляю - Слишком большая длинна истории - 256; 0, 225 = divmod(225, 227) 2022-06-05 09:55 Удаляю - Слишком большая длинна истории - 249; 0, 226 = divmod(226, 227) 2022-06-05 09:55 Удаляю - Слишком большая длинна истории - 244; 0, 226 = divmod(226, 227) 2022-06-05 09:55 Удаляю - Слишком большая длинна истории - 354; 0, 159 = divmod(159, 227) 2022-06-05 09:55 Удаляю - Слишком большая длинна истории - 244; 0, 226 = divmod(226, 227) 2022-06-05 09:56 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:56 Удаляю - Слишком большая длинна истории - 244; 0, 226 = divmod(226, 227) 2022-06-05 09:56 Удаляю - Слишком большая длинна истории - 252; 0, 225 = divmod(225, 227) 2022-06-05 09:56 Удаляю - Слишком большая длинна истории - 248; 0, 226 = divmod(226, 227) 2022-06-05 09:56 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:56 Удаляю - Слишком большая длинна истории - 222; 0, 226 = divmod(226, 227) 2022-06-05 09:56 Удаляю - Слишком большая длинна истории - 240; 0, 226 = divmod(226, 227) 2022-06-05 09:56 Удаляю - Слишком большая длинна истории - 215; 0, 226 = divmod(226, 227) 2022-06-05 09:56 Удаляю - Слишком большая длинна истории - 208; 0, 226 = divmod(226, 227) 2022-06-05 09:57 Удаляю - Слишком большая длинна истории - 248; 0, 226 = divmod(226, 227) 2022-06-05 09:57 Удаляю - Слишком большая длинна истории - 220; 0, 226 = divmod(226, 227) 2022-06-05 09:57 Удаляю - Слишком большая длинна истории - 249; 0, 226 = divmod(226, 227) 2022-06-05 09:57 Удаляю - Слишком большая длинна истории - 246; 0, 226 = divmod(226, 227) 2022-06-05 09:57 Удаляю - Слишком большая длинна истории - 246; 0, 226 = divmod(226, 227) 2022-06-05 09:57 Удаляю - Слишком большая длинна истории - 249; 0, 226 = divmod(226, 227) 2022-06-05 09:57 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:57 Удаляю - Слишком большая длинна истории - 216; 0, 226 = divmod(226, 227) 2022-06-05 09:57 Удаляю - Слишком большая длинна истории - 214; 0, 226 = divmod(226, 227) 2022-06-05 09:58 Удаляю - Слишком большая длинна истории - 244; 0, 226 = divmod(226, 227) 2022-06-05 09:58 Удаляю - Слишком большая длинна истории - 343; 0, 179 = divmod(179, 227) 2022-06-05 09:58 Удаляю - Слишком большая длинна истории - 244; 0, 226 = divmod(226, 227) 2022-06-05 09:58 Удаляю - Слишком большая длинна истории - 245; 0, 226 = divmod(226, 227) 2022-06-05 09:58 Удаляю - Слишком большая длинна истории - 201; 0, 226 = divmod(226, 227) 2022-06-05 09:58 Удаляю - Слишком большая длинна истории - 274; 0, 223 = divmod(223, 227) 2022-06-05 09:58 Удаляю - Слишком большая длинна истории - 250; 0, 225 = divmod(225, 227) 2022-06-05 09:58 Удаляю - Слишком большая длинна истории - 243; 0, 226 = divmod(226, 227) 2022-06-05 09:58 Удаляю - Слишком большая длинна истории - 244; 0, 226 = divmod(226, 227) 2022-06-05 09:59 Удаляю - Слишком большая длинна истории - 217; 0, 226 = divmod(226, 227) 2022-06-05 09:59 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:59 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:59 Удаляю - Слишком большая длинна истории - 248; 0, 226 = divmod(226, 227) 2022-06-05 09:59 Удаляю - Слишком большая длинна истории - 251; 0, 225 = divmod(225, 227) 2022-06-05 09:59 Удаляю - Слишком большая длинна истории - 233; 0, 226 = divmod(226, 227) 2022-06-05 09:59 Удаляю - Слишком большая длинна истории - 1225; 0, 140 = divmod(140, 227) 2022-06-05 09:59 Удаляю - Слишком большая длинна истории - 252; 0, 225 = divmod(225, 227)

Кажется, что никогда это условие не сможем пройти.

WLM1ke commented 2 years ago

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

Snest1 commented 2 years ago

Спасибо!

А как добиться, чтобы "в тестовых данных примеров было количество тикеров * количество тестовых дней" ? Я для пробы удалил из портфеля "короткие" бумаги: Бумага Дней REGN-RM 245 CMCSA-RM 247 GILD-RM 247 TMUS-RM 247 EBAY-RM 248 GPS-RM 248 MRNA-RM 248 ORCL-RM 248 PTR-RM 248 TTWO-RM 248 BCSB 273 FIXP 283

Следующая ABT-RM 304

Т.е. самая "короткая" по истории бумага у меня сейчас ABT-RM 304 дня истории, но это не помогло. Стал разбираться дальше, обнаружил что:

population.count=64 (около 40 моделей умерли после нового релиза - это нормально) 64 торговых дня назад было Timestamp('2022-02-04 00:00:00') поэтому в evolve.py цикл organism.evaluate_fitness запускается начиная с 2022-02-04 В evaluate_fitness создается Model c end = 2022-02-04, которая не проходит quality_metrics - вываливается по Exception из-за TooLongHistoryError и следующий цикл опять начинается с 2022-02-04 Вываливается в _eval_llh, так как len(loader.dataset) у меня всегда меньше n_tickers, а должно быть либо равно, либо в n-раз больше.

Здесь я уже теряюсь... Видимо len(loader.dataset) это и есть "кол-во примеров в тестовых данных". Но не могу понять, что ему мешает стать равным n*tickers.

WLM1ke commented 2 years ago

Допустим у вас 64 модели, прогноз делается на 21 (это дефолтная установка, не знаю как у вам), и пусть у вас самая короткая история по бумаге 245 дня. Далее важен параметр модели history_days (он у всех моделей разный).

Собственно, чтобы все было нормально, минимальная история по бумаге (245) должна быть больше, чем 64 + 21 + history_days, то есть в этом условном случае history_days < 245 - 64 - 21 = 160.

Snest1 commented 2 years ago

Еще раз спасибо! Проверил в коллекции моделей, нашел три штуки, у которых history_days был больше максимально возможного для меня по формуле из Вашего предыдущего ответа (259 -64 - 21 = 174), и удалил их.

Сейчас у всех Родителей (и в СУБД и в логе эволюции) history_days в допустимых пределах (<174 +3. т.к. три модели удалил ), но у всех Потомков history_days в диапазоне 200 - 250+ (наблюдал в течении 10 минут). Таким образом ни одна из родительских моделей не может перестроиться на изменившееся кол-во тикеров. Нормально ли это?
Не проще начать обучение заново? Или нужно просто время, чтобы рандом попал в допустимый history_days для Потомков?

WLM1ke commented 2 years ago

С потомками, что странное. Они должны иметь history_days в некой симметричной окрестности около history_days родителя. То есть условно, если у вас у родителя 174 дня, то примерно в половине случаев будут получаться потомки с меньшим количеством дней.

Snest1 commented 2 years ago

Похоже это возвращет функция _to_bounds из chromosome.py. Для key=history_days на вход ей подается parent1[key]=126.37099112505452, parent2[key]=163.5959968136489, scale=0.12803687993289598, diff=-4.766173583851928, raw_value=126.30325883031003, gene.upper_bound=None, gene.lower_bound=189,

Возвращает child[key]=251.69674116968997

WLM1ke commented 2 years ago

Понял, моя ошибка - запушил изменения

Snest1 commented 2 years ago

Спасибо, помогло. Откатил модели на состояние "до обновлений от 3июня", запустил эволюцию, буду наблюдать.