WLM1ke / poptimizer

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

mongo DocumentTooLarge. pickled forecasts ~55MB #57

Closed RomaKoks closed 2 years ago

RomaKoks commented 3 years ago
INFO:EventBus:AppStarted(timestamp=datetime.datetime(2021, 5, 12, 5, 35, 25, 243556))
Forecasts: 90it [03:55,  2.62s/it]
Traceback (most recent call last):
  File "path_to\poptimizer\poptimizer\store\database.py", line 48, in __setitem__
    self._collection.replace_one({"_id": key}, value, upsert=True)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\collection.py", line 939, in replace_one
    common.validate_ok_for_replace(replacement)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\common.py", line 539, in validate_ok_for_replace
    validate_is_mapping("replacement", replacement)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\common.py", line 494, in validate_is_mapping
    raise TypeError("%s must be an instance of dict, bson.son.SON, or "
TypeError: replacement must be an instance of dict, bson.son.SON, or any other type that inherits from collections.Mapping

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "path_to\poptimizer\loop.py", line 13, in opt
    optimize(date)
  File "path_to\poptimizer\poptimizer\__main__.py", line 30, in optimize
    opt = Optimizer(port)
  File "path_to\poptimizer\poptimizer\portfolio\optimizer.py", line 53, in __init__
    self._metrics = metrics.MetricsResample(portfolio)
  File "path_to\poptimizer\poptimizer\portfolio\metrics.py", line 138, in __init__
    for forecast in evolve.get_forecasts(tickers, date):
  File "path_to\poptimizer\poptimizer\evolve\forecaster.py", line 70, in get_forecasts
    mongodb[FORECAST] = forecasts
  File "path_to\poptimizer\poptimizer\store\database.py", line 51, in __setitem__
    self._collection.replace_one({"_id": key}, value, upsert=True)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\collection.py", line 943, in replace_one
    self._update_retryable(
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\collection.py", line 868, in _update_retryable
    return self.__database.client._retryable_write(
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\mongo_client.py", line 1498, in _retryable_write
    return self._retry_with_session(retryable, func, s, None)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\mongo_client.py", line 1384, in _retry_with_session
    return self._retry_internal(retryable, func, session, bulk)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\mongo_client.py", line 1416, in _retry_internal
    return func(session, sock_info, retryable)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\collection.py", line 860, in _update
    return self._update(
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\collection.py", line 829, in _update
    result = sock_info.command(
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\pool.py", line 699, in command
    self._raise_connection_failure(error)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\pool.py", line 683, in command
    return command(self, dbname, spec, slave_ok,
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\network.py", line 135, in command
    message._raise_document_too_large(
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\message.py", line 1092, in _raise_document_too_large
    raise DocumentTooLarge("%r command document too large" % (operation,))
pymongo.errors.DocumentTooLarge: 'update' command document too large

вот что он https://github.com/WLM1ke/poptimizer/blob/09150f017a57f65dbc2a8b944229d60ef151176a/poptimizer/store/database.py#L50 содержит: https://yadi.sk/d/XMyiA7TrG0XqrQ

WLM1ke commented 3 years ago

В последней версии количество моделей сокращено до 80, а у вас 90. Нужно в ручную грохнуть 10 моделей. После этого должно работать.

RomaKoks commented 3 years ago

Видимо, последнюю версию вы еще не запушили: https://github.com/WLM1ke/poptimizer/blob/09150f017a57f65dbc2a8b944229d60ef151176a/poptimizer/evolve/evolve.py#L11

WLM1ke commented 3 years ago

Залил

RomaKoks commented 3 years ago

C 80 та же ошибка.

Forecasts: 80it [03:27,  2.60s/it]
Имеется 80 организмов из 80

Traceback (most recent call last):
  File "path_to\poptimizer\poptimizer\store\database.py", line 48, in __setitem__
    self._collection.replace_one({"_id": key}, value, upsert=True)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\collection.py", line 939, in replace_one
    common.validate_ok_for_replace(replacement)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\common.py", line 539, in validate_ok_for_replace
    validate_is_mapping("replacement", replacement)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\common.py", line 494, in validate_is_mapping
    raise TypeError("%s must be an instance of dict, bson.son.SON, or "
TypeError: replacement must be an instance of dict, bson.son.SON, or any other type that inherits from collections.Mapping

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "path_to\poptimizer\loop.py", line 13, in opt
    optimize(date)
  File "path_to\poptimizer\poptimizer\__main__.py", line 23, in optimize
    opt = Optimizer(port)
  File "path_to\poptimizer\poptimizer\portfolio\optimizer.py", line 53, in __init__
    self._metrics = metrics.MetricsResample(portfolio)
  File "path_to\poptimizer\poptimizer\portfolio\metrics.py", line 145, in __init__
    for forecast in evolve.get_forecasts(tickers, date):
  File "path_to\poptimizer\poptimizer\evolve\forecaster.py", line 70, in get_forecasts
    mongodb[FORECAST] = forecasts
  File "path_to\poptimizer\poptimizer\store\database.py", line 51, in __setitem__
    self._collection.replace_one({"_id": key}, value, upsert=True)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\collection.py", line 943, in replace_one
    self._update_retryable(
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\collection.py", line 868, in _update_retryable
    return self.__database.client._retryable_write(
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\mongo_client.py", line 1498, in _retryable_write
    return self._retry_with_session(retryable, func, s, None)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\mongo_client.py", line 1384, in _retry_with_session
    return self._retry_internal(retryable, func, session, bulk)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\mongo_client.py", line 1416, in _retry_internal
    return func(session, sock_info, retryable)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\collection.py", line 860, in _update
    return self._update(
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\collection.py", line 829, in _update
    result = sock_info.command(
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\pool.py", line 699, in command
    self._raise_connection_failure(error)
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\pool.py", line 683, in command
    return command(self, dbname, spec, slave_ok,
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\network.py", line 135, in command
    message._raise_document_too_large(
  File "path_to\anaconda3\envs\finan\lib\site-packages\pymongo\message.py", line 1092, in _raise_document_too_large
    raise DocumentTooLarge("%r command document too large" % (operation,))
pymongo.errors.DocumentTooLarge: 'update' command document too large
WLM1ke commented 3 years ago

А у вас дефолтное количество бумаг в портфеле или есть какие-то дополнительные бумаги?

RomaKoks commented 3 years ago

Есть дополнительные

WLM1ke commented 3 years ago

А много их?

RomaKoks commented 3 years ago

всего 175: ('AAL-RM', 'ABBV-RM', 'ABRD', 'AFKS', 'AFLT', 'AGRO', 'AKRN', 'ALRS', 'AMD-RM', 'AMEZ', 'APTK', 'AQUA', 'ASSB', 'ATVI-RM', 'AVGO-RM', 'BA-RM', 'BANE', 'BANEP', 'BELU', 'BIDU-RM', 'BIIB-RM', 'BISVP', 'BMY-RM', 'BRZL', 'BSPB', 'CBOM', 'CHEP', 'CHMF', 'CNTL', 'CNTLP', 'DIOD', 'DSKY', 'EELT', 'ENPG', 'ENRU', 'ETLN', 'FB-RM', 'FDX-RM', 'FEES', 'FESH', 'FIVE', 'FLOT', 'FXCN', 'FXDE', 'FXKZ', 'FXRB', 'FXRU', 'GAZP', 'GCHE', 'GEMA', 'GLTR', 'GMKN', 'GTRK', 'HHRU', 'HIMCP', 'HPQ-RM', 'HYDR', 'INGR', 'IRAO', 'IRGZ', 'IRKT', 'ISKJ', 'KBSB', 'KLSB', 'KMAZ', 'KRKNP', 'KRSB', 'KRSBP', 'KZOS', 'LIFE', 'LKOH', 'LNTA', 'LNZL', 'LNZLP', 'LSNG', 'LSNGP', 'LSRG', 'MAGN', 'MAIL', 'MDMG', 'MGNT', 'MGTS', 'MGTSP', 'MOEX', 'MRKC', 'MRKP', 'MRKS', 'MRKU', 'MRKV', 'MRKY', 'MRKZ', 'MRSB', 'MSNG', 'MSRS', 'MSTT', 'MTLR', 'MTLRP', 'MTSS', 'MU-RM', 'MVID', 'NEM-RM', 'NFAZ', 'NFLX-RM', 'NKHP', 'NKNC', 'NKNCP', 'NLMK', 'NMTP', 'NSVZ', 'NVTK', 'OGKB', 'ORUP', 'OZON', 'PHOR', 'PIKK', 'PLZL', 'PMSBP', 'POGR', 'POLY', 'QCOM-RM', 'RASP', 'RBCM', 'RGSS', 'RNFT', 'ROLO', 'ROSN', 'RSTI', 'RSTIP', 'RTKM', 'RTKMP', 'RUGR', 'RZSB', 'SBER', 'SBERP', 'SBRB', 'SELG', 'SELGP', 'SFIN', 'SIBN', 'SMLT', 'SNGS', 'SNGSP', 'STSBP', 'SVAV', 'T-RM', 'TATN', 'TATNP', 'TGKA', 'TGKB', 'TGKBP', 'TGKD', 'TGKN', 'TORSP', 'TRMK', 'TRNFP', 'TSLA-RM', 'TTLK', 'TWTR-RM', 'UBER-RM', 'UNAC', 'UNKL', 'UPRO', 'USBN', 'UWGN', 'V-RM', 'VSMO', 'VTBB', 'VTBM', 'VTBR', 'VTRS-RM', 'WMT-RM', 'XOM-RM', 'YAKG', 'YNDX', 'ZILL')

WLM1ke commented 3 years ago

То есть на 15 больше дефолта. Вам видимо нужно еще снизить популяцию моделей. По грубой прикидке до 60.

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

RomaKoks commented 3 years ago

а отчего так много места занимают предикты? может быть как-то изменить формат хранения?

WLM1ke commented 3 years ago

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

RomaKoks commented 3 years ago

можно добавить компрессию pickle строки, хотя 175х175 не звучит как что-то большое...

RomaKoks commented 3 years ago

80 (моделей) 175 (тикеров) ^2 8 (вес float64 в байтах) / 1024^2 (приведение к мегабайтам) = 18,69 MB то есть еще чем-то занята половина.

WLM1ke commented 3 years ago

Тот размер, который вы в названии написали, выглядит странно. Там кроме ковариационный матрицы еще доходности хранятся (линейно зависит от количества) и еще кое-что по мелочи (грубо константно). Основное - это ковариационная матрица для каждого прогноза. Документ действительно для 160 бумаг чуть меньше 16 МБ и у меня влезает в ограничение на размер документа MongoDB. Увеличение до 175 бумаг накинет где-то 20% к размеру - по идее должно не влезать.

WLM1ke commented 3 years ago

https://github.com/WLM1ke/poptimizer/blob/75f23cb9b8f3576f2d2f1dac4586fe91433fc1d2/poptimizer/dl/forecast.py#L26

RomaKoks commented 3 years ago

Проверил, сжатие не сильно поможет: sys.getsizeof(pickle.dumps(value)) / 1024 / 1024 18.959022521972656 sys.getsizeof(bz2.compress(pickle.dumps(value))) / 1024 / 1024 14.727270126342773

WLM1ke commented 3 years ago

Естественно - там флоаты достаточно слабо связанные между собой.

WLM1ke commented 3 years ago

И тут у вас цифра ближе на правду похожа - 18 мб

RomaKoks commented 3 years ago

В названии я указал размер текстового файла. А какое ограничение у mongo?

WLM1ke commented 3 years ago

16 мб

RomaKoks commented 3 years ago

Получается, с компрессией влезет)

WLM1ke commented 3 years ago

Я бумаги добавляю каждую неделю - переставит влезать.

RomaKoks commented 3 years ago

В таком случае нужно ковариацию, наверное, на лету считать. А для чего она нужна, к тому же для каждой модели?

WLM1ke commented 3 years ago

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

RomaKoks commented 3 years ago

А можно ссылки на примеры использования в коде? А то я могу довольно долго искать)

WLM1ke commented 3 years ago

https://github.com/WLM1ke/poptimizer/blob/master/poptimizer/portfolio/metrics.py

WLM1ke commented 3 years ago

Весь файл про это

RomaKoks commented 3 years ago

А почему бы не хранить именно метрики?

WLM1ke commented 3 years ago

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

RomaKoks commented 3 years ago

Тогда можно хранить прогнозы отдельно (по каждой модели), а не одним документом

WLM1ke commented 3 years ago

В принципе, да. Но я пока решил идти по простому пути - сокращать количество моделей. В перспективе может перепишу на раздельное хранение.

Нашел 15 дополнительных бумаг в вашем списке, буду потихоньку добавлять, тем более часть я и так хотел включить: AGRO ASSB BISVP ETLN FIVE KBSB KRSB KRSBP KZOS MGTS NKNC POLY RZSB STSBP TORSP

RomaKoks commented 3 years ago

оффтоп: оператор @ что делает? https://github.com/WLM1ke/poptimizer/blob/75f23cb9b8f3576f2d2f1dac4586fe91433fc1d2/poptimizer/portfolio/metrics.py#L71 =)

RomaKoks commented 3 years ago

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

WLM1ke commented 3 years ago

Все верно матричное умножение - это расчет дисперсии портфеля на основе ковариация активов и их весов

RomaKoks commented 3 years ago

Еще оффтоп, заметил такой момент, что тикерам, отсутствующим в портфеле (у которых вес 0), отдается слишком большое предпочтение при рекомендации покупки. То есть я хочу сказать, что, возможно, учёт веса в портфеле нужно как-то "сгладить".

WLM1ke commented 3 years ago

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

Можно попробовать отключить в следующей редакции.