← Voltar

Восстановление сохраненной стратегии

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

**пример:**открываем позицию - сохраняем - на следующий день восстанавливаем... позицию восстановить можно, а вот как саму сделку, чтобы выполнить пересчет параметров стратеги, тот же PnL?

Este tópico foi útil?

Compartilhar tópico

Comentários (27)

Entrar ou Criar conta, Faça login ou cadastre-se para deixar um comentário

RomSunZ
RomSunZ

Есть описание в хелпе в разделе про стратегии. Называется загрузка заявок и сделок.

Andrii
Andrii Autor

RomSunZ: Есть описание в хелпе в разделе про стратегии. Называется загрузка заявок и сделок. читал, там описывается как восстановить заявки(Order), но не все заявки заканчиваются удачно, нет метода восстановить сделки(Trade) нет реального примера? или ему нужно скормить сделки, которые прошли, или все можно, а он сам поймет?

devruss
devruss

Andrii:

RomSunZ: Есть описание в хелпе в разделе про стратегии. Называется загрузка заявок и сделок. читал, там описывается как восстановить заявки(Order), но не все заявки заканчиваются удачно, нет метода восстановить сделки(Trade) нет реального примера? или ему нужно скормить сделки, которые прошли, или все можно, а он сам поймет?

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

Можно даже обобщить задачу: Есть 2 робота, они торгуют один и тот же инструмент (допустим RiZ4), но на разных таймфреймах, стратегиях и т.д. Произошло отключение (обрыв связи или отключение overnight), на момент обрыва первый был long 100 контрактов, второй 400.

  1. Как после восстановления соединения восстановить правильную позицию у роботов? (именно сделки, а не ордера)
  2. Как эти 2 робота поделят общую позицию по RiZ4 (500: +100 (1) +400 (2))? (т.е. как роботом понять какая часть сделок его, а какая нет)

Уверен, что те, кто силен в программировании уже реализовали данную задачу, поделитесь пожалуйста методом решения

RomSunZ
RomSunZ

Ну так сохраняйте сделки/ордера во временный файл или в коллекцию после их появления с идентификатором для каждой стратегии, а потом восстанавливайте их от туда.

Andrii
Andrii Autor

RomSunZ: Ну так сохраняйте сделки/ордера во временный файл или в коллекцию после их появления с идентификатором для каждой стратегии, а потом восстанавливайте их от туда. каким образом восстанавливать сделки? или даже как правильно все реализовать? пример есть?

RomSunZ
RomSunZ

Поищите здесь на форуме были примеры почти готовой обертки восстановления и сохранения стратегии где-то год назад или около того

Andrii
Andrii Autor

RomSunZ: Поищите здесь на форуме были примеры почти готовой обертки восстановления и сохранения стратегии где-то год назад или около того

восстановить стратегию нахожу, но мне бы ее сделки

нахожу

this.StatisticManager.AddNewOrder();
this.ProcessNewOrders();

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

на текущий момент this.ProcessNewOrders(); не отрабатывает

Andrii
Andrii Autor

так же есть AttachOrder, но как будет корректно не особо понятно, хочется опытное мнение

Andrii
Andrii Autor

секретные разработки?

JaguarFX
JaguarFX

Andrii: секретные разработки? Поиск по форуму - лучшее лекарство от подозрительности)) Так мной было описан полный набор функций для сохранения заявок/сделок по стратегии в базу данных в разделе по развитию S#.Shell Ссылка

Сейчас по прошествии 2-х месяцев могу сказать что все работает на 100%. Однако я внес следующие изменения в подход к сохранению позиций:

  1. заменил сохранение в MS SQL на сохранение в MS Access, используя универсальный интерфейс OleDb; все-таки Access проше, систему не грузит и занимает в 10ки раз меньше места,
  2. вообще прекратил сохранять заявки и сделки, сохраняю только позиции; для хранения позиций пришлось сделать отдельный класс TradeBook, в котором как раз расписано для каждой стратегии какую она имеет долю в "общем пироге" открытой по инструменту позиции - это к вопросу devruss'а относительно учета результатов работы нескольких стратегий с одним счетом и инструментом.
Andrii
Andrii Autor

lebedevsrg:

Andrii: секретные разработки? Поиск по форуму - лучшее лекарство от подозрительности)) Так мной было описан полный набор функций для сохранения заявок/сделок по стратегии в базу данных в разделе по развитию S#.Shell Ссылка

Сейчас по прошествии 2-х месяцев могу сказать что все работает на 100%. Однако я внес следующие изменения в подход к сохранению позиций:

  1. заменил сохранение в MS SQL на сохранение в MS Access, используя универсальный интерфейс OleDb; все-таки Access проше, систему не грузит и занимает в 10ки раз меньше места,
  2. вообще прекратил сохранять заявки и сделки, сохраняю только позиции; для транения позиций пришлось сделать отдельный класс TradeBook, в котором как раз расписано для каждой стратегии какую она имеет долю в "общем пироге" открытой по инструменту позиции - это к вопросу devruss'а относительно учета результатов работы нескольких стратегий с одним счетом и инструментом.

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

Но в целом я понял, используется AttachOrder и его сделками... этого и хотел

RomSunZ
RomSunZ

Супер секретная разработка по сохранению/восстановлению стратегий: http://stocksharp.com/forum/4045/S--Shell--Manual/?page=2

devruss
devruss

Большое спасибо за ответ. Так как я не программист, мне потребовалось несколько дней, чтобы разобраться в коде Есть несколько вопросов:

  • Ваша идея восстановления позиции состоит в том, чтобы при каждом запуске заново грузить все сделки и по ним считать позицию. Для этого сделки хранятся в БД. Это абсолютно верно, что из сделок можно получить поозицию, но, как мне кажется, избыточно. Был ли рассмотрен следующий подход: при совершении сделки (partial fill/ full fill), меняется позиция. Робот узнает об изменении позиции только от feedback от биржи о статусе исполненного ордера (ну либо если ордер лимитный, и робот знает, что пришел confirmation о том, что он выставлен, + цена прошла уровень ордера, то автоматом считаем ордер исполненным - нужно для HFT). Так вот, как только робот узнает об изменении позиции, вместо того, чтобы тут же сохранять сделку в БД он просто записывает в файл позицию. При старте робота, не нужно выгружать все сделки, а только сообщить стартовую позицию и все.

  • Встает вопрос, за какой период нужно выгружать сделки. Если робот торгует длинные таймфремы, то это может быть и несколько месяцев. А это дофига сделок, если есть в портфеле есть стратегии торгующие high frequency и low frequency.

  • В чем потенциальные минусы предложенного подхода в сравнении с уже реализованным выше. Может я упускаю какие-то тонкости?

JaguarFX
JaguarFX

Тут в каждом подходе есть свои плюсы и свои минусы.

  1. Восстановление позиций через восстановление заявок/сделок Очевидный плюс - получение полноценной статистики и PnL на основе свойств стратегии. Минус - при большом числе заявок/сделок перфоманс при старте падает. Кроме того у меня при количестве заявок прим. от 2000 / сделок от 3000 стали возникать проблемы с тем, что предварительный запрос на очистку таблиц от старых записей в целях исключения дублирований стал прерываться при старте следующего по коду запроса на запись, и как результат пошла мультипликация записей. Возможно там для решения проблемы нужно было перейти на асинхронный вызов и дожидаться выполнения первого запроса из кода, но я к этому моменту осознал избыточность для моих интересов данного подхода и остановил запись и восстановление заявок/сделок. Перешел на подход №2.

  2. Восстановление позиций через восстановление информации о позициях Очевидный плюс - сохранение в БД только самой важной информации. Далее, если объект заявка содержит только одно поле UserOrderId для хранения информации о сигнале на открытие позиции, то в собственном классе по позициям можно создать несколько полей для последующего использовании при принятии решения о закрытии позиции (н-р сигнал, тайм-фрейм) и подсчете доходов (н-р при торговле фичами размер резерва капитала, даты/спреды ролл-оверов). Минус - необходимость создания собственного класса для хранения позиций и встраивание его работы в работу стратегии. Кроме того стандартные статистика и PnL по стратегии теряют свою актуальность. Статистика конечно не столь важна, а вот для расчета PnL пришлось написать отдельную функцию. Для моей стратегии правда это было и так необходимо сделать, так как PnL с учетом колебаний рыночной стоимости не интересен, а интересна мгновенная и общая годовая доходность работы по сигналам.

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

devruss
devruss

lebedevsrg: Тут в каждом подходе есть свои плюсы и свои минусы.

  1. Восстановление позиций через восстановление заявок/сделок

  2. Восстановление позиций через восстановление информации о позициях

Я тоже создал свои классы и под хранение инфы по трейдам (не позициям), а также по правильному подсчету pnl, mark-to-market pnl, доходности и т.д. Поправь меня если я не прав, но по ссылке выше ты рассказал именно про реализацию подхода 1 - восстановления позиции через сделки. Тогда вопрос: Как реализовать именно вараинт 2? По большому счету тут SQL не нужно. Достаточно 1 файл с элементами типа {Strategy ID/Name, Position}. И при перезапуске робота считывать эти данные и сообщать роботу с какой позицией он стартует. Тогда в общем случае и логику менять не надо, так как робот просто сразу переходит в состояние long/short N контрактов как бы пропуская фазу набора позиции.

Andrii
Andrii Autor

devruss:

lebedevsrg: Тут в каждом подходе есть свои плюсы и свои минусы.

  1. Восстановление позиций через восстановление заявок/сделок

  2. Восстановление позиций через восстановление информации о позициях

Я тоже создал свои классы и под хранение инфы по трейдам (не позициям), а также по правильному подсчету pnl, mark-to-market pnl, доходности и т.д. Поправь меня если я не прав, но по ссылке выше ты рассказал именно про реализацию подхода 1 - восстановления позиции через сделки. Тогда вопрос: Как реализовать именно вараинт 2? По большому счету тут SQL не нужно. Достаточно 1 файл с элементами типа {Strategy ID/Name, Position}. И при перезапуске робота считывать эти данные и сообщать роботу с какой позицией он стартует. Тогда в общем случае и логику менять не надо, так как робот просто сразу переходит в состояние long/short N контрактов как бы пропуская фазу набора позиции.

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

покажешь свои классы "подсчету pnl, mark-to-market pnl, доходности и т.д." ?

devruss
devruss

по большому счету да, сохраняешь состояние стратегии и восстанавливаешь его при создании. Но тогда теряется статистика стратегии... но если у тебя свои классы по ее подсчету, то можно себе позволить покажешь свои классы "подсчету pnl, mark-to-market pnl, доходности и т.д." ?

В данном топике это оффтоп. На самом деле, я даже классы не делал. На каждом тике пересчитываю стоимость позиции (== mark to market), из этого получается и pnl при закрытии позиции (реализованный mark to market) и доходность (pnl/capital)

RomSunZ
RomSunZ

А что мешает взять из БД позицию/ордера/сделки по последней открытой позиции, а сохраненные данные по предыдущим сделкам/позициям/ордерам использовать для анализа работы системы?

devruss
devruss

RomSunZ: А что мешает взять из БД позицию/ордера/сделки по последней открытой позиции, а сохраненные данные по предыдущим сделкам/позициям/ордерам использовать для анализа работы системы?

Я не понял, как можно передать роботу позицию? AttachOrder функция есть, а как указать текущую позицию при старте?

Andrii
Andrii Autor

devruss:

RomSunZ: А что мешает взять из БД позицию/ордера/сделки по последней открытой позиции, а сохраненные данные по предыдущим сделкам/позициям/ордерам использовать для анализа работы системы?

Я не понял, как можно передать роботу позицию? AttachOrder функция есть, а как указать текущую позицию при старте?

Strategy str = new MyStrategy() { Position = 5 };
RomSunZ
RomSunZ

devruss:

RomSunZ: А что мешает взять из БД позицию/ордера/сделки по последней открытой позиции, а сохраненные данные по предыдущим сделкам/позициям/ордерам использовать для анализа работы системы?

Я не понял, как можно передать роботу позицию? AttachOrder функция есть, а как указать текущую позицию при старте?

Какой сайз и какое направление было у ордера, который через AttachOrder присоединяется к стратегии, такая поза у стратегии и будет. Если несколько ордеров, то позиция суммируется.

devruss
devruss

RomSunZ: Какой сайз и какое направление было у ордера, который через AttachOrder присоединяется к стратегии, такая поза у стратегии и будет. Если несколько ордеров, то позиция суммируется.

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

  1. Не нужна БД. Достаточно 1 файла на стратегию с позицией
  2. Быстрый старт

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

Andrii
Andrii Autor

вообще все зависит от стратегии и нужных данных, но более универсальный метод - писать только сделки открывающие позицию:

открыли позицию - записали сделку - закрыли позицию - удалили 2 сделки

алгоритм сложный, но оставляет только сделки по открытым позициям

devruss
devruss

Andrii: вообще все зависит от стратегии и нужных данных, но более универсальный метод - писать только сделки открывающие позицию:

открыли позицию - записали сделку - закрыли позицию - удалили 2 сделки

алгоритм сложный, но оставляет только сделки по открытым позициям

Удалять - это сложно. А вот изменилась позиция - записали в файл, изменилась опять - опять записали, мне кажется как-то проще и fail proof

Marco
Marco

devruss: Удалять - это сложно. А вот изменилась позиция - записали в файл, изменилась опять - опять записали, мне кажется как-то проще и fail proof

Мы например так и делаем. При каждом изменении позиции стратегии по инструментам сохраняются в .csv файл. При старте стратегии - восстанавливаются (без сделок и ордеров).

Код:



        protected List<Position> LoadPositions(string fileName)
        {
            List<Position> positions = new List<Position>();

            using (StreamReader stream = new StreamReader(fileName))
            {
                using (CsvReader reader = new CsvReader(stream))
                {
                    reader.Configuration.Delimiter = ",";
                    reader.Configuration.HasHeaderRecord = false;

                    //var records = reader.GetRecords<MyTrade>();
                    while (reader.Read())
                    {
                        try
                        {
                            string securityCode = reader.GetField<string>(0);
                            decimal value = reader.GetField<decimal>(1);
                            string portfolioName = reader.GetField<string>(2);

                            Portfolio portfolio = Connector.Portfolios.FirstOrDefault((Portfolio pf) =>
                            {
                                return pf.Name == portfolioName;
                            });
                            if (portfolio == null)
                            {
                                this.AddWarningLog("Portfolio with name {0} not found.", portfolioName);
                                continue;
                            }

                            Security security = Connector.Securities.FirstOrDefault((Security sec) =>
                            {
                                return sec.Code == securityCode;
                            });
                            if (security == null)
                            {
                                this.AddWarningLog("Security with code {0} not found.", securityCode);
                                continue;
                            }

                            Position position = new Position()
                            {
                                Portfolio = portfolio,
                                Security = security,
                                CurrentValue = value
                            };

                            positions.Add(position);
                        }
                        catch (Exception ex)
                        {
                            this.AddErrorLog("Ошибка загрузки позиции: {0}", ex.Message);
                        }
                    }
                }
            }

            return positions;
        }


        protected void LoadPositions()
        {
            if (!SavePositionsEnabled)
                return;

            string fileName = GetPersistentFileName("positions", "csv");
            this.AddInfoLog("Загрузка позиций из файла {0}", fileName);

            if (File.Exists(fileName))
            {
                List<Position> positions;

                lock (lockPositionsFile)
                {
                    positions = LoadPositions(fileName);
                }

                PositionManager.Positions = positions;
                RaiseParametersChanged("Position");
            }
            else
            {
                this.AddWarningLog("Файла с сохраненными позициями {0} не существует.", fileName);
            }
        }

        protected void SavePositions(IEnumerable<Position> positions, string fileName, bool append)
        {
            using (StreamWriter stream = new StreamWriter(fileName, append))
            {
                using (CsvWriter writer = new CsvWriter(stream))
                {
                    writer.Configuration.Delimiter = ",";
                    writer.Configuration.HasHeaderRecord = false;

                    foreach (Position position in positions)
                    {
                        try
                        {
                            writer.WriteField(position.Security.Code, true);
                            writer.WriteField(position.CurrentValue);
                            writer.WriteField(position.Portfolio.Name);
                            writer.NextRecord();
                        }
                        catch (Exception ex)
                        {
                            this.AddErrorLog("Ошибка сохранения позиции {0}: {1}", position.ToString(), ex.Message);
                        }
                    }

                    stream.Flush();
                }
            }
        }

        protected void SavePositions(IEnumerable<Position> positions)
        {
            if (!SavePositionsEnabled)
                return;

            string fileName = GetPersistentFileName("positions", "csv");
            this.AddInfoLog("Сохранение позиций в файл {0}", fileName);

            lock (lockPositionsFile)
            {
                SavePositions(PositionManager.Positions, fileName, false);
            }
        }


                PositionsChanged += (IEnumerable<Position> positions) =>
                {
                    SavePositions(positions);
                };

SavePositions() вызывается при изменении позиции и остановке стратегии. LoadPositions - в Strategy.OnStarted().

Метод спорный, но у нас работает. При этом ломается расчет PnL (мы им все равно не пользуемся, PnL стратегий считается скриптами на Python'е по сохраненным трейдам раз в день после клиринга - StockSharp все равно PnL считает неправильно...)

Andrii
Andrii Autor

Marco:

сказал а, говори б...

как вы считает PnL, полученный и нереализованный?

Marco
Marco

как вы считает PnL, полученный и нереализованный?

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

  • получают с биржи индикативный курс доллара на закрытие дня
  • считают вариационку по стратегиям
  • выгружают отчет в Excel

В течение дня PnL мы не считаем. Опять же, в чем считать PnL? Фьючерс на индекс RTS номинирован в долларах, рублевый PnL по нему в течение дня посчитать корректно вообще нельзя, т.к. курс валюты становится известен только на закрытие дня.