Блог им. WinterMute

Торговая система своими руками. Часть 10. IoC, защита от сбоев, логгирование.

    • 26 октября 2017, 12:32
    • |
    • k100
  • Еще

     Привет всем! В предыдущих статьях я описывал свой тестер, разработанный на C#, и, несколько раз подчёркивал, что переключение между двумя режимами (тестирование/торговля) может быть простым. Код стратегий не должен зависеть от того, кто поставщик маркет-даты и куда уходят заявки – в тестовую базу или на сервер брокера. Конечно, это лишь один из подходов, и кому-то он покажется странным, но, главное его достоинство заключается в том, что тестирование приближается к реальности, что даёт более достоверные результаты. Вопрос в следующем: как, имея один и тот же код, получать разные по функциональности программы? Один из вариантов – использовать инверсию управления и внедрение зависимостей! Об этом сегодня и пойдёт речь.

    Приведу пример нехорошего (иногда, говорят – с запашком) кода:

class Strategy
{
   public Strategy()
   {
     var mgr = new TestOrderManadger();
     mgr.PlaceOrder(...);
   }
}

     Здесь плохо то, что класс Strategy зависит от класса TestOrderManadger. В такой реализации нельзя начать использовать какой-нибудь другой менеджер заявок (AnotherOrderManadger) без перекомпиляции библиотеки с классом Strategy. Тем более тут нарушается принцип единства ответственности – класс Strategy, помимо своей прямой обязанности, также, создаёт внутри себя зависимости. Чтобы исправить ситуацию, можно использовать интерфейсы:

interface IOrderMandger
{
   void PlaceOrder();
}

class TestOrderManadger : IOrderMandger
{
   public void PlaceOrder(){}
}

class Strategy
{
   public Strategy(IOrderMandger orderMandger)
   {
     var mgr = orderMandger;
     mgr.PlaceOrder(...);
   }
}

     И, где-то далее в коде:

Strategy mySuperStrategy = 
  new Strategy(new TestOrderManadger());

     В этом варианте Strategy зависит не от конкретного класса TestOrderManadger, а от абстракции (от интерфейса IOrderMandger), и не важно, какая будет реализация. Класс Strategy больше не занимается построением зависимостей (больше не создаёт внутри себя объекты). Это и есть инверсия управления (Inversion of Control, IoC) – модули верхнего уровня не должны зависеть от модулей нижнего уровня. Но всё-таки у такого кода ещё есть “запашок”. Проблема в строчке:

Strategy mySuperStrategy = 
   new Strategy(new TestOrderManadger());

     Точнее, в операторе new. Используя оператор new, мы всё ещё явно создаём конкретные объекты. Хорошо бы кому-то делегировать, всё, что связанно с построением зависимостей и с созданием объектов. В идеале, в коде, вообще не должно быть оператора new (за исключением простых, скалярных типов). Механизм, реализующий это, и называется  “внедрение зависимостей” (Dependency Injection, DI). Где-то, в самом начале программы (или даже во внешнем файле) регистрируются все интерфейсы и их реализации, а дальше, разрешение зависимостей и создание объектов берёт на себя IoC контейнер.

     Под .Net есть целая куча IoC контейнеров, я использую Autofac, но принципы регистрации и применения у всех схожи. Сначала, нужно настроить контейнер – указать, какой класс реализует тот или иной интерфейс. Вот, примерно так выглядит настройка:

static class AutofacContainer
{
   public static IContainer ConfigureContainer(
     bool isTestMode)
   {
     ContainerBuilder builder =  GetContainerBuilder();

     if (isTestMode)
     {
       builder
       .RegisterType<TestOrderManadger()
       .As<IOrderManadger>()
       .SingleInstance();
       ...
     }
     else
     {
       builder
       .RegisterType<AnotherOrderManadger()
       .As<IOrderManadger>()
       .SingleInstance();
       ...
     }

     return builder.Build();
   }
}

     Смысл в том, что от переданного в конфигуратор аргумента (isTestMode) зависит, будет ли программа работать в режиме бэктеста или в режиме реальных торгов. Таким же образом регистрируются все необходимые классы: сервисы, репозитории и пр., и даже здесь не используется оператор new, и нигде в коде. В контейнере можно провести дополнительные настройки, связанные с механизмом созданием объекта и его жизненным циклом. Например, в коде выше, команда SingleInstance(), указывает, что объект должен быть создан в единственном экземпляре.

     Теперь, после настройки контейнера, код, приведенный чуть выше:

Strategy mySuperStrategy =
   new Strategy(new TestOrderManadger());

     Можно заменить на:

var container = AutofacContainer
     .ConfigureContainer(isTestMode: false);

Strategy mySuperStrategy = 
     container.Resolve<Strategy>();

     Контейнер сам подставит в конструктор класса Strategy нужные зависимости (нужную реализацию интерфейса IOrderMandger).

     В предыдущих постах, я рассказывал, что использую фабрику стратегий – класс, который по некоему ID стратегии выдаёт ту или иную реализацию интерфейса IStrategy. Чтобы это заработало, в IoC контейнере, нужно зарегистрировать все стратегии и присвоить им уникальный идентификатор:

builder.RegisterType<MySuperStrategy>()
   .Keyed<IStrategy>(1);

builder.RegisterType<AnoterSimpleStrategy>()
   .Keyed<IStrategy>(2);

builder.RegisterType<MagicStrategy>()
   .Keyed<IStrategy>(3);
...

     И, код самой фабрики:

public class StrategyFactory
{
   private IComponentContext container;
   ...
   
   public IStrategy CreateStrategy(
     int strategyID)
   {
     IStrategy strategy =
       container
       .ResolveKeyed<IStrategy>(strategyID);
     return strategy;
   }
}

     Фабрика берёт на себя ответственность за создание объектов стратегий. По сути, это небольшая обёртка над контейнером. Тут же могут быть какие-то дополнительные настройки стратегии.

     Сам контейнер инициализируется в момент старта программы, и, в зависимости от значения переменной isTestMode, будут использоваться те или иные объекты, и от этого будет зависеть режим работы – тестовый или торговый.


     Теперь, хотел сказать по поводу логгирования. По сути, это протоколирование того, что программа делает. В простейшем случае можно было бы выводить сообщения в консоль. Но, если использовать отдельную библиотеку можно получить ряд преимуществ: настраиваемый формат лога, вывод в отдельный фай (или архив, БД), настраиваемые уровни лога (критическая ошибка, просто сообщение и т.п.), поддержка многопоточности. Я использую NLog под .Net, но вариантов много (вплоть до аспектно-ориентированных логгеров, реализующих сквозную функциональность).

     Вот так может выглядеть настройка логгера внутри IoC контейнера:

var fileName = @"C:\Logfile\Log.txt";

var config = new LoggingConfiguration();
var fileTarget = new FileTarget();
config.AddTarget("file", fileTarget);
fileTarget.FileName = fileName;
fileTarget.Layout = @"${newline}${level} 
   ${longdate} ${callsite} ${message}
   ${exception:format=tostring} ${newline}";

var rule = new LoggingRule("*", 
                LogLevel.Debug, 
                fileTarget);

config.LoggingRules.Add(rule);
LogManager.Configuration = config;

builder
 .RegisterInstance<ILogger(LogManager
      .GetLogger("Logger"))
      .SingleInstance();

     Далее, в коде программы, можно осуществлять логгирование:

try
{
   ...
}
catch (Exception e)
{
   logger.Error(e, "Ошибка работы программы");
}

     При этом, если произойдёт ошибка, и программа попадёт в блок catch, в лог запишется подробная информация об этой ошибке: когда она произошла, код ошибки, в каком модуле и т.д.

     Контроль за ботами и за работой программы в целом – это важная составляющая архитектуры системы. На первых парах лучше перестараться, чем потерять деньги на какой-нибудь ерунде. Важно использовать блоки try..catch и логгирование. Также, я думаю, в серьёзных системах есть независимая от ботов программа, которая может быть даже запускается на другом терминале. Эта программа контролирует работу ботов, и у неё есть возможность отключить их, или даже вырубить сеть, если что-то пошло не так: полилось слишком много заявок, или сильные изменения в портфеле, или пришла аномальная марке-дата. В любом случае, своим ботам надо доверять, а это достигается, в том числе, за счёт необходимого программного контроля за ними.

     На этом всё! Всем спасибо! ;-)

344 | ★12
9 комментариев


avatar
Пробовал изучать С++ когда-то. Для меня оказалось слишком сложно. Автор, ты гений)))
avatar
crazyFakir,)))

Для разнообразия
avatar
Vasek, главное верить в себя
avatar
Вопрос к автору. Есть желание научиться самому написать робота, но в программировании полный ноль. С чего начать обучение?
avatar
Иван Петров, найти хорошего напарника или команду! А если серьёзно, то всё зависит ещё от возраста. Как говорил мой любимый писатель — Чарльз Буковски — «В двадцать пять гением может быть любой. В пятьдесят для этого уже что-то надо сделать.»
Определитесь кем вы хотите быть — дизайнером, администратором БД, верстальщиком, серверным программистом, тимлидом… Это как в rpg игре — вначале надо выбрать себе класс персонажа. Также, надо понимать, что базовые конструкции любого языка, правила ООП и паттерны — это только начало, знать только это — ничего не даст. Каждый соверменный язык — это в первую очередь стек технологий — набор фреймворков и библиоте — умение пользоваться ими даёт главное конкурентное преимущество программиста.
Чтобы научиться — надо иметь громадную силу воли, с ноля в одиночку — это практически подвиг. Выбор языка не так важен — c#, java — по большому счёту одно и тоже. python, js — другие, там по другому, я бы не начинал с них.
Вам нужно приготовиться много читать и пробовать, если выберете C# — начните с основ .Net, с паттернов проектирования, с WPF, можно ASP MVC. Если выберете Java — то однозначно Spring, это в первую очередь DI, IoC, ORM, и весь стек J2EE -EJB, JNDI, JPA, JSF или SWING и куча всего прочего..
Верить в себя и не отчаиваться… Если будут вопросы — пишите!
avatar
k100, Благодарю за столь объемный ответ. В принципе я не хочу становиться ни верстальщиком, ни администратором БД и т.д. Есть допустим простенькая торговая система, хочу ее автоматизировать, но не с целью заработка, а для того чтобы разобраться как это делается. Пусть это будет на языке C#. Но без теории что то начинать бессмысленно. В сети море литературы и курсов. Посоветуйте пожалуйста с какой литературы начать изучать основы и в каком направлении двигаться, чтобы понять что вы пишите в своих постах))
avatar
Иван Петров, есть неплохие ресурсы: вот этот и этот
Есть хабр: я частенько, если что-то ищу, пишу в поисковике (гугл или яндекс), например: «хабр c# события», или «хабр c# лямбды» или «хабр c# ООП» и т.п. Если c# — то нужна среда разработки, например бесплатная visual studion community. Может, сперва, стоит немного освоиться, написать несколько простых проектиков (обычно, хорошие находки в коде, кочуют из одного проекта в другой) — это подготовит, немного, к написанию бота.
avatar
k100, ок, спасибо за советы
avatar

Читайте на SMART-LAB:
Фото
Актуальный состав портфеля и взгляд на рынок 2026: по-прежнему 0% позитива.
Добрый вечер! С момента предыдущего поста, касающегося моего портфеля, прошел квартал.  Пришло время актуализировать его состав. Также поделюсь...
Фото
Биткоин попробует разыграть «треугольную карту»?
«Цифровое золото» прорвало верхнюю границу восходящего треугольника на уровне 94 500 и сейчас тестирует пробитую горизонталь, формируя серию...
Фото
Индикатор Fractal: торговые сигналы и робот для OsEngine. Видео
В этом видео разбираем индикатор Fractal Билла Вильямса — один из самых известных инструментов в трейдинге. Покажем, как формируются фракталы,...
Фото
Стратегия 2026 по рынку акций от Mozgovik Research: трудный год, но, возможно, последний год низких цен
Сегодня у меня первый день официального отпуска. За окном темная звездная ночь, яркая белая луна, +24С и шум волн Андаманского моря. Неудачный...

теги блога k100

....все тэги



UPDONW
Новый дизайн