using System; using TSLab.DataSource; using TSLab.Script; using TSLab.Script.Handlers; using TSLab.Script.Helpers; using TSLab.Script.Optimization; namespace MySystem0 { public class MySystem0 : IExternalScript { public OptimProperty CompressInterval = new OptimProperty(15, 1, 1440, 1); public OptimProperty Period1 = new OptimProperty(9, 1, 24, 1); public OptimProperty Period2 = new OptimProperty(56, 24, 96, 4); public OptimProperty TrailEnable = new OptimProperty(0.5, 0.1, 2, 0.1); public OptimProperty TrailLoss = new OptimProperty(1, 0.1, 2, 0.1); public OptimProperty StopLoss = new OptimProperty(1, 0.1, 2, 0.1); readonly TrailStop _trail = new TrailStop(); public void Execute(IContext context, ISecurity security) { // Сжать исходный интервал var days = security.CompressTo(new Interval(CompressInterval, security.IntervalBase)); if (days == null || days.Bars.Count <= 1) return; // Определить таймфрейм сжатого ценового ряда TimeSpan oneDay; switch (security.IntervalBase) { case DataIntervals.DAYS: oneDay = new TimeSpan(CompressInterval, 0, 0, 0); break; case DataIntervals.MINUTE: oneDay = new TimeSpan(0, 0, CompressInterval, 0); break; case DataIntervals.SECONDS: case DataIntervals.TICK: default: oneDay = new TimeSpan(0, 0, 0, CompressInterval); break; } // Задать параметры скользящего стопа значениями соовтетствующих параметров скрипта _trail.StopLoss = StopLoss; _trail.TrailEnable = TrailEnable; _trail.TrailLoss = TrailLoss; // Рассчитать простые скользящие средние по сжатому ценовому ряду var closePrices = days.ClosePrices; var ma1 = context.GetData("SMA", new[] { Period1.ToString() }, () => Series.SMA(closePrices, Period1)); var ma2 = context.GetData("SMA", new[] { Period2.ToString() }, () => Series.SMA(closePrices, Period2)); int maSign = 0; // Взаиморасположение скользящих средних: // -1 - ma1 ниже ma2 // 0 - ma1 равна ma2 // 1 - ma1 выше ma2 int iDay = 1; // Индекс начального бара на сжатом ряде. // Начинать с 1 поскольку сигнал система генерирует по предыдущему бару. // Напомню, индексация массивов начинается с 0. Т.е. первый элемент массива имеет индекс 0. int barsCount = security.Bars.Count; // Количество баров (свечек) в исходном источнике if (!security.IsLastBarUsed) barsCount--; // Нововведение в 1.1.20. Последний бар не всегда надо использовать // Основной цикл по не сжатым барам (не напиться бы. Шутка :-) for (int i = context.TradeFromBar; i < barsCount; i++) { var bar = security.Bars[i]; // Текущий несжатый бар var dayBar = days.Bars[iDay]; // Текущий сжатый бар // Синхронизировать бары по времени if (bar.Date < dayBar.Date) // Дата (и время) начала несжатого бара меньше даты (и времени) начала сжатого. continue; // Пропустить эту итерацию, поскольку еще не дошли до начального бара while (bar.Date >= dayBar.Date + oneDay) // Пока дата несжатого бара больше или равна дате следующего сжатого бара dayBar = days.Bars[++iDay]; // Взять следующий сжатый бар // Т.е. переходить к следующему сжатому бару до тех пор пока текущий не сжатый бар не окажется в его границах // Как правило потребуется только одна итерация этого цикла, но если context.TradeFromBar > 0, то итераций может быть несколько. // Торговая система "пересечение двух скользящих средних" bool signal; // Сигнал - пересечение скользящих средних. // Определить взаиморасположение скользящих средних. int s = Math.Sign(ma1[iDay-1] - ma2[iDay-1]); // Вычесть значения скользящих средних и определить знак полученной разницы: // -1 если ma1[iDay-1] < ma2[iDay-1] ma1 ниже ma2 // 0 если ma1[iDay-1] == ma2[iDay-1] ma1 равна ma2 // 1 если ma1[iDay-1] > ma2[iDay-1] ma1 выше ma2 // Использовать значения на предыдущем сжатом баре. Текущий использовать нельзя, иначе система будет заглядывать в будущее if (maSign != s && s != 0) { // Взаиморасположение средних изменилось и они не равны друг другу maSign = s; // Запомнить новое взаиморасположение signal = true; // По торговой системе есть сигнал на открытие позиции } else signal = false; // Сигнала больше нет, он живет только одну итерацию цикла, т.е. 1 исходный таймфрейм // Исполнение сигнала торговой системы IPosition position = security.Positions.GetLastActiveForSignal("E"); if (position != null) // Позиция уже открыта, рассчитать и установить скользящий стоп (стоп-лимит) position.CloseAtStop(i + 1, _trail.Execute(position, i), "X"); else // Открытой позиции нет if (signal) // Есть сигнал от торговой системы if (maSign > 0) // Покупать security.Positions.BuyAtMarket(i + 1, 1, "E"); else // Продавать security.Positions.SellAtMarket(i + 1, 1, "E"); } if (context.IsOptimization) // Если идет оптимизация, то оставшуюся часть не выполнять return; // Визуализация IPane mainPane = context.First; // Первая панель графика // Разжать средние, чтобы можно было их показать на графике ma1 = days.Decompress(ma1, DecompressMethodWithDef.Method3); ma2 = days.Decompress(ma2, DecompressMethodWithDef.Method3); // Добавить графики mainPane.AddList("sma." + Period1, ma1, ListStyles.LINE_WO_ZERO, 0x00aa00, LineStyles.SOLID, PaneSides.RIGHT); mainPane.AddList("sma." + Period2, ma2, ListStyles.LINE_WO_ZERO, 0xaa0000, LineStyles.SOLID, PaneSides.RIGHT); // Обновить точность координатной оси справа mainPane.UpdatePrecision(PaneSides.RIGHT, security.Decimals); } } }