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;
}
}
}