Блог им. WinterMute

Торговая система своими руками. Часть 8. Формирование закрытых позиций и подсчёт статистики.

    • 09 октября 2017, 15:14
    • |
    • k100
  • Еще

     Добрый день. В предыдущих частях я описывал, как на C# сделал собственный тестер, применяя объектно-ориентированный подход, рассказывал про интерфейсы, про их реализации, и, рассказывал про работу с БД. На данный момент осталось совсем немного. В этом топике я опишу вариант расчёта результатов работы стратегии.

     Чтобы не запутаться, даже не читая предыдущие топики, поясню, что есть и к чему надо придти. Есть стратегии – это некий объект программы, который выставляет заявки на основе получаемой маркет-даты. Заявки (Order) регистрируются системой. Также, регистрируются сделки прошедшие по заявке (каждая заявка имеет список сделок — List<Trades> trades). После прогона стратегии, все заявки и сделки сохраняются в БД, и после, их можно извлечь и посчитать по ним статистику работы стратегии. По сути, эта статистика состоит из двух аспектов: сами закрытые позиции и оценка эффективности на их основе. Начнём с первого. Вот интерфейс, который принимает заявки со сделками, и, выдаёт, собственно, список закрытых позиций:

interface IClosePositionManager
{
   List<ClosePosition> ClosePositions (List<Order> orders);
}

     Класс ClosePosition, имеет следующие поля: id закрытой позиции, объём, сделка инициатор, закрывающая сделка, нарастающий итог, профит/убыток, комиссия, время в позиции и т.д.

     Как реализовать этот интерфейс? Сделки проходят по разным ценам, и чтобы пазл сложился нужно последовательно выполнить расчёт – по сути, надо свести разнонаправленные сделки по своим ценам. Для расчёта использую очереди и рекурсию. Беру все сделки по символу и ранжирую их по порядку (по Id сделки). Далее, начинаю считать. Беру первую сделку (объём, цена) и помещаю её в очередь, беру следующую, если направление то же, то и её помещаю в очередь. Если направление противоположное, то формируется закрытая позиция. Тут важен объём. В простом случае, объёмы текущей сделки и сделки из очереди равны – они схлопнулись и текущая сделка и сделка из очереди ушли, все по своим ценам. Если объём текущей меньше чем из очереди, то текущая уходит, а объём из очереди кратно уменьшается. А если объём текущей больше чем общём из очереди – то текущая, как бы проваливается ниже, кратно поглощая сделки из очереди. Код, если кому надо – приведу. Соответственно по каждой закрытой позиции есть цена покупки и продажи, на основе которых считается профит/убыток.

     Теперь вторая составляющая статистики – оценка эффективности работы стратегии на основе её закрытых позиций:

interface IResultManager
{
   IndexOfEffectiveness Calc(List<ClosePosition> closePositions);
}

     Класс IndexOfEffectiveness включает в себя набор стандартных (да и вообще, каких угодно) показателей: общая доходность, DrawDown, PF, RF, средняя сделка, Шарп и т.д.:

class IndexOfEffectiveness
{
   [DisplayName("Общая доходность")]
   public BuySellResult<double> Yield { get; set; }

   [DisplayName("Максимальный DrawDown")]
   public BuySellResult<double> MDD { get; set; }

   [DisplayName("Profit Factor")]
   public BuySellResult<double> PF { get; set; }

   [DisplayName("Recovery Factor")]
   public BuySellResult<double> RF { get; set; }

   … и т.д.
}

     Тут перечислены все необходимые статистические показатели. Важно отметить, что в качестве возвращаемого типа каждого показателя используется обобщённый класс-обёртка BuySellResult, состоящий из трёх полей – покупки, продажи, всё вместе. Это нужно чтобы разделить, например, Максимальный DrawDown по покупкам и продажам.

     Что касается самой реализации интерфейса IResultManager (метод Calc), то тут, нудно и последовательно, выполняется расчет всех показателей:

public IndexOfEffectiveness Calc(List<ClosePosition> closePositions)
{
   var buyPositions = closePositions
       .Where(cp => cp.Init.Order
           .Action == OrderAction.Buy);

   var sellPositions = closePositions
      .Where(cp => cp.Init.Order
            .Action == OrderAction.Sell);

   var tradesCount = new BuySellResult<int>()
   {
     Buy = buyPositions.Count(),
     Sell = sellPositions.Count(),
     All = closePositions.Count()
   };

   var winPositions = new BuySellResult<IEnumerable<ClosePosition>>()
   {
     Buy = buyPositions.Where(cp => 
        Math.Sign(cp.ClearProfit) == 1),
     Sell = sellPositions.Where(cp => 
        Math.Sign(cp.ClearProfit) == 1),
     All = closePositions.Where(cp => 
        Math.Sign(cp.ClearProfit) == 1)
   };

   var winCount = new BuySellResult<int>()
   {
     Buy = winPositions.Buy.Any() ? winPositions.Buy.Count() : 0,
     Sell = winPositions.Sell.Any() ? winPositions.Sell.Count() : 0,
     All = winPositions.All.Any() ? winPositions.All.Count() : 0
   };
   ... и т.д.
}

     И всё остальное в том же духе. В конце создаётся класс IndexOfEffectiveness и ему передаются все рассчитанные выше показатели.

     Кстати в классе IndexOfEffectiveness используются атрибуты (в частности DisplayName из стандартного пространства имён System.ComponentModel). Атрибуты (или аннотации в Java) делают программирование более декларативным, по сути, это простейший вид рефлексии. Например, в DisplayName я указываю, как должна называться соответствующая колонка в таблице отображающий результаты. При инициализации таблиц в visual studio, этот атрибут подхватывается. Также, например, атрибуты используют для задания ограничений, которые автоматически проверяются в определённый момент. Можно реализовать собственные атрибуты с оригинальным поведением.

     После прогона стратегии, рассчитываются закрытые позиции и показатели эффективности. Эти данные передаются в форму для визуализации результата. Про саму визуализацию я напишу в следующем посте.

★17
4 комментария
какой коннектор используете?
avatar
Борис Литвинов, Смартком, но старался делать систему так, чтобы минимум зависеть от коннектора.
avatar
k100, сейчас на транзак пытаюсь уйти, транзак не пробывали?
Вообще как вамскорость на смарт ком?
Изучаю C#. Уже многое понятно и по сути написать простого бота знаний хватит, а вот терминал, работу с БД и тестер пока нет!
avatar
Борис Литвинов, Смартком хорош, проект живой, недавно вышла новая, 4 версия.
По с# — если будут вопросы, задавайте, я с удовольствием проконсультирую)
avatar

теги блога k100

....все тэги



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