using System.ComponentModel; using System.Linq; using TSLab.Script; using TSLab.Script.Handlers; using TSLab.Script.Handlers.Options; using static GanovCubes.FileLogger; namespace GanovCubes { #region CubeDescription [System.Obsolete] [HandlerCategory("Ganov Cubes. Different")] [HelperName("ComissionAdv")] [Description("Кубик является модифицированное версией базового кубика комиссии и применяет к стратегии общую комиссию, " + "включающую в себя:" + "\n-- абсолютную комиссию, то есть комиссию за каждую сделку в баз.единицах цены актива, например 5 руб/на сделку на 1 лот;" + "\n-- относительную комисиию, то есть комиссию за каждую сделку в % от суммы сделки, например, 0.003% от суммы сделки;" + "\n-- комиссию за предоставление заемных средств при торговле в Long (c плечом) или в Short: считается исходя из кол-ва дней пользования заемными средствами," + " для коротких позиций считается со всей суммы сделки, для длинных - как число лотов минус 1, помноженное на цену лота" + "\n-- резерв, то есть коэффициент, на который умножаются все указанные комиссии")] [HelperLink("http://forum.tslab.ru/ubb/ubbthreads.php?ubb=showflat&Number=87233&page=1", "Страница на форуме TSLab", "ru-ru")] [InputsCount(1)] [Input(0, TemplateTypes.SECURITY, Name = "Sec")] [OutputsCount(0)] #endregion public class ComissionAdv : ICommissionHandler, IHandler, ISecurityInputs, IContextUses { #region Properties public IContext Context { get; set; } /// /// Комиссия за каждую сделку в пунктах (абсолютная комиссия) /// [HandlerParameter(true, "0.00", NotOptimized = false, Name = "Комиссия, абс.", IsVisibleInBlock = true, Min = "0", NumberDecimalDigits = 5)] [Description("Комиссия за каждую сделку в пунктах(абсолютная комиссия) за 1 лот, например 5 руб / на сделку на 1 лот")] public double ComisAbs { get; set; } = 0; /// /// Комиссия за каждую сделку в % от суммы сделки (относительная комиссия) /// [HandlerParameter(true, "0.00", NotOptimized = false, Name = "Комиссия, %", IsVisibleInBlock = true, Min = "0", NumberDecimalDigits = 5)] [Description("Комиссия за каждую сделку в % от суммы сделки (относительная комиссия), например, 0.003% от суммы сделки")] public double ComisPct { get; set; } = 0; /// /// Проскальзывание в пунктах /// [HandlerParameter(true, "0", NotOptimized = false, Name = "Проскальзывание", IsVisibleInBlock = true, Min = "0", NumberDecimalDigits = 1)] [Description("Проскальзывание в пунктах. В данной настройке указывается величина проскальзывания, которая может случаться в реальном рынке из-за различного" + " рода причина: скорость выставления заявки, резкие движения рынка, ликвидность инструмента, спред инструмента и т.д")] public double Slippage { get; set; } = 0; /// /// Маржа, % (стоимость заемных средств) /// [HandlerParameter(true, "0.00", NotOptimized = false, Name = "Стоимость денег, %", IsVisibleInBlock = true, Min = "0", NumberDecimalDigits = 3)] [Description("Стоимость заемных средств в процентах при торговле в Long (c плечом) или в Short")] public double MarginPct { get; set; } = 0; /// /// Резерв для тестирования /// [HandlerParameter(true, "2.0", NotOptimized = false, Name = "Резерв для тестирования", IsVisibleInBlock = true, Min = "1", NumberDecimalDigits = 2)] [Description("Все комиссии для оценки устойчивости системы умножаются на данный коэффициент")] public double TestReserve { get; set; } = 2; /// /// Стоимость пункта инструмента /// [HandlerParameter(true, "1.00", NotOptimized = false, Name = "Стоимость пункта инструмента", IsVisibleInBlock = true, Min = "0", NumberDecimalDigits = 5)] [Description("Стоимость пункта инструмента в валюте комиссий, используется для перевода абс. комиссии в пункты инструмента")] public double PointPrice { get; set; } = 1; /// /// Использовать данные по инструменту /// [HandlerParameter(true, "true", NotOptimized = true, Name = "Использовать данные по инструменту", IsVisibleInBlock = false)] [Description("При установленной опции комиссия и стоимость пункта получается из кубиков TickerBrokerComisData, TickerExchComisData, TickerSecPointPrice." + " При снятой опции используются значения, указанные в настройках кубика. При пересчете в лаборатории, кроме режима оптимизации кубик сообщает" + " какие величины комиссий будут использованы")] public bool UseSecData { get; set; } = true; /// /// Идентификатор данных по комиссии брокера /// [HandlerParameter(true, "FinamComisData", NotOptimized = true, Name = "Идентификатор данных по комиссии брокера", IsVisibleInBlock = false)] [Description("Идентификатор данных по комиссии биржи. Определяет название файла, из которого будут получены необходимые данные по комиссии брокера." + " Данные используются в случае если настройка \"Использовать данные по инструменту\" равна True")] public string BrokerDataID { get; set; } = "FinamComisData"; /// /// Идентификатор данных по комиссии биржи /// [HandlerParameter(true, "MoexComisData", NotOptimized = true, Name = "Идентификатор данных по комиссии биржи", IsVisibleInBlock = false)] [Description("Идентификатор данных по комиссии биржи. Определяет название файла, из которого будут получены необходимые данные по комиссии биржи." + " Данные используются в случае если настройка \"Использовать данные по инструменту\" равна True")] public string ExhangeDataID { get; set; } = "MoexComisData"; /// /// Выводить информацию в лог ТСЛаб /// [HandlerParameter(true, "true", NotOptimized = true, Name = "Выводить данные по комиссии в лог", IsVisibleInBlock = false)] [Description("При установке данной опции на каждом пересчете кубик будет выводить в лог информацию об используемой информации по комиссиям и" + " стоимости инструмента (источник данных и значения)")] public bool ShowComissionDataToLog { get; set; } = true; /// /// Вывести данные в лог /// [HandlerParameter(true, "false", NotOptimized = false, Name = "Вывести данные в лог", IsVisibleInBlock = false)] [Description("При установленной опции данные по работе кубика будут выведены в лог")] public bool WriteLog { get; set; } = false; #endregion /// /// Метод применяет комиссию к сделкам в режиме лаборатории /// /// Инструмент public void Execute(ISecurity sec) { if (UseSecData) { // Кубики получения данных по инструменту var h_BrokerComis = new TickerBrokerComisData() { Context = Context, WriteLog = WriteLog }; var h_ExchangeComis = new TickerExchComisData() { Context = Context, WriteLog = WriteLog }; var h_SecPointPrice = new TickerSecPointPrice() { Context = Context, WriteLog = WriteLog }; // Идентификаторы данных var brokerDataID = Context.GetRefTypeCollection(BrokerDataID, Context.BarsCount); var exchangeDataID = Context.GetRefTypeCollection(ExhangeDataID, Context.BarsCount); // Получаем комиссию брокера h_BrokerComis.ComisData = BrokerComisDataType.BrokerAbs; var brokerAbs = h_BrokerComis.Execute(sec, brokerDataID).FirstOrDefault(); h_BrokerComis.ComisData = BrokerComisDataType.BrokerPct; var brokerPct = h_BrokerComis.Execute(sec, brokerDataID).FirstOrDefault(); // Получаем комиссию биржи h_ExchangeComis.ComisData = ExchComisDataType.TotalAbs; var exchangeAbs = h_ExchangeComis.Execute(sec, exchangeDataID).FirstOrDefault(); h_ExchangeComis.ComisData = ExchComisDataType.TotalPct; var exchangePct = h_ExchangeComis.Execute(sec, exchangeDataID).FirstOrDefault(); ComisAbs = brokerAbs + exchangeAbs; ComisPct = brokerPct + exchangePct; PointPrice = h_SecPointPrice.Execute(sec).FirstOrDefault(); if (WriteLog) LogDebug($"Данные инструмента: brokerAbs {brokerAbs}, brokerPct {brokerPct}, exchangeAbs {exchangeAbs}, exchangePct {exchangePct}"); } if (ShowComissionDataToLog && !Context.IsOptimization) { Context.Log($"Используются данные {(UseSecData ? "инструмента" : "кубика")}, " + $"ComisAbs: {ComisAbs}, ComisPct: {ComisPct}, PointPrice: {PointPrice}", MessageType.Info, true); } sec.Commission = new CommissionDelegate(CommissionDelegate); } protected virtual double CommissionDelegate(IPosition pos, double price, double shares, bool isEntry, bool isPart) { var comisCom = price * ComisPct / 100.0 * shares; if (!isEntry && !isPart) { var totalDays = (pos.ExitBar.Date - pos.EntryBar.Date).TotalDays; var lots = pos.MaxShares; if (pos.IsLong) { var sharesOrigin = pos.SharesOrigin; if (sharesOrigin <= 1.0) lots = 0.0; else lots *= 1.0 - 1.0 / sharesOrigin; } if (shares > 0.0) comisCom += pos.AverageEntryPrice * MarginPct / 100.0 * totalDays / 365.0 * lots; } if (Slippage > 0) comisCom += Slippage; if (ComisAbs > 0) comisCom += ComisAbs / (PointPrice > 0 ? PointPrice : 1); return comisCom * TestReserve; } } }