/// <summary> /// Основной метод бота /// </summary> /// <param name="ctx">Контекст TSLab</param> /// <param name="sec">Инструмент</param> public void Execute(IContext ctx, ISecurity sec) { // таймер для определения времени выполнения скрипта var sw = Stopwatch.StartNew(); #region расчет индикаторов IList <double> upChannel = ctx.GetData("UpChannel", new string[] { PeriodChannel.ToString() }, () => Series.Highest(sec.HighPrices, PeriodChannel)); IList <double> downChannel = ctx.GetData("DownChannel", new string[] { PeriodChannel.ToString() }, () => Series.Lowest(sec.LowPrices, PeriodChannel)); #endregion #region первый и последний бар для расчетов // первый бар, используемый для расчетов int startBar = Math.Max(PeriodChannel + 1, ctx.TradeFromBar); // последний бар, используемый для расчетов var barsCount = sec.Bars.Count; if (!ctx.IsLastBarClosed) { barsCount--; } #endregion // цены закрытия инструмента var closePrices = sec.ClosePrices; #region Создание объекта для трейлинг стопа _trailStopHnd = new TrailStop() { StopLoss = StopLossPct, TrailEnable = TrailLossEnablePct, TrailLoss = TrailLossPct }; #endregion #region Торговый цикл for (int i = startBar; i < barsCount; i++) { #region Формируем торговые сигналы // сигнал входа в лонг, сигнал активен в течение 2-х бар при выхода цены закрытия из канала bool signalLE = closePrices[i] > upChannel[i - 1] && closePrices[i - 2] <= upChannel[i - 3]; // сигнал входа в шорт, сигнал активен в течение 2-х бар при выхода цены закрытия из канала bool signalSE = closePrices[i] < downChannel[i - 1] && closePrices[i - 2] >= downChannel[i - 3]; // сигнал выхода из лонга bool signalLX = closePrices[i] < downChannel[i - 1]; // сигнал выхода из шорта bool signalSX = closePrices[i] > upChannel[i - 1]; #endregion #region Получаем активные позиции var lePosition = sec.Positions.GetLastActiveForSignal("LE", i); var sePosition = sec.Positions.GetLastActiveForSignal("SE", i); var openPositions = sec.Positions.GetActiveForBar(i).ToList(); #endregion #region Выход из позиции, реверс позиции, установка стоп-лосса и тейк-профита if (openPositions.Count != 0) { foreach (IPosition pos in openPositions) { switch (pos.EntrySignalName) { #region обработка позиции лонг LE case "LE": //условие выхода из лонга if (signalLX) { //закрытие лонга по лимиту pos.CloseAtPriceSlip(i + 1, closePrices[i], Slippage, "LX"); // условие реверса позиции if (_regimeBot != RegimeBot.OnlyShort && _regimeBot != RegimeBot.OnlyClosePosition) { // открытие шорта по лимиту для реверса позиции sec.SellAtPriceSlip(i + 1, 1, closePrices[i], Slippage, "SE"); } } // если не было сигнала выхода, то выставляем стопы и профиты else { // выставление стоп-лосса if (OnStopLoss || OnBreakevenStop) { SetStopLoss(pos, i); } // выставление тейк-профотита if (OnTakeProfit) { SetTakeProfit(pos, i); } } break; #endregion #region обработка позиции шорт SE case "SE": // условие выхода из шорта if (signalSX) { // закрытие шорта по лимиту pos.CloseAtPriceSlip(i + 1, closePrices[i], Slippage, "SX"); // условие реверса позиции if (_regimeBot != RegimeBot.OnlyShort && _regimeBot != RegimeBot.OnlyClosePosition) { // открытие лонга по лимиту для реверса позиции sec.BuyAtPriceSlip(i + 1, 1, closePrices[i], Slippage, "LE"); } } // если не было сигналов на выход, то выставляем стоп-лосс и тейк-профит else { // выставление стопа-лосса if (OnStopLoss || OnBreakevenStop) { SetStopLoss(pos, i); } // выставление тейк-профита if (OnTakeProfit) { SetTakeProfit(pos, i); } } break; #endregion #region обработка прочих позиций default: ctx.Log($"Обработка прочих позиций {pos.EntrySignalName}", MessageType.Warning); break; #endregion } } } #endregion if (_regimeBot == RegimeBot.OnlyClosePosition) { continue; } #region Вход в первую позицию // если нет ни одной активной позиции, то проверяем условие входа в первую позицию if (openPositions.Count == 0) { // лонг if (signalLE && _regimeBot != RegimeBot.OnlyShort) { sec.BuyAtPriceSlip(i + 1, 1, closePrices[i], Slippage, "LE"); } // шорт else if (signalSE && _regimeBot != RegimeBot.OnlyLong) { sec.SellAtPriceSlip(i + 1, 1, closePrices[i], Slippage, "SE"); } } #endregion } #endregion #region Прорисовка графиков // Если идет процесс оптимизации, то графики рисовать не нужно, это замедляет работу if (ctx.IsOptimization) { return; } IGraphPane pane = ctx.First ?? ctx.CreateGraphPane("First", "First"); Color colorCandle = ScriptColors.Black; pane.AddList(sec.Symbol, sec, CandleStyles.BAR_CANDLE, colorCandle, PaneSides.RIGHT); var lineUpChannel = pane.AddList(string.Format($"UpChannel ({PeriodChannel,0})"), upChannel, ListStyles.LINE, ScriptColors.Red, LineStyles.SOLID, PaneSides.RIGHT); //lineUpChannel.Thickness = 2; var lineDownChannel = pane.AddList(string.Format($"DwChannel ({PeriodChannel,0}"), downChannel, ListStyles.LINE, ScriptColors.Blue, LineStyles.SOLID, PaneSides.RIGHT); //lineDownChannel.Thickness = 2; #endregion #region Вывод в лог // Вывод в лог времени выполнения скрипта только в режиме Лаборатория if (!ctx.Runtime.IsAgentMode) { ctx.Log($"Скрипт выполнен за время: {sw.Elapsed}", MessageType.Info, true); } #endregion }