/*================================================================================ * Стратегия: WMA cross * Платформа: TSLab версия 1.1.8.0 * Дата создания: 19.07.2010 * Реализовано: Laber *================================================================================*/ using System; using System.Collections.Generic; using TSLab.Script; using TSLab.Script.Handlers; using TSLab.Script.Optimization; using TSLab.Script.Helpers; namespace TSLab.WMA_cross { //================================================================================ public class WMA_cross : IExternalScript { // используем переменные-флаги для сигналов public bool bBuy; // флаг сигнала открытия длинной позиции public bool bSell; // флаг сигнала закрытия длинной позиции public bool bShort; // флаг сигнала открытия короткой позиции public bool bCover; // флаг сигнала закрытия короткой позиции public IPosition LongPos, ShortPos; //================================================================================ // функция вычисления WMA (последовательность значений) public IList GenWMA(IList source, int Period) { //var wma = new double[source.Count]; IList nWMA = new List(source.Count); double vWMA = 0; double wma1 = 0; int W = 0; // Расчет весов в знаменателе int Wv = 0; // Расчет весов в числителе //Вычисляем сумму весов for(int j = 0; j < Period; j++) W=W+j+1; for(int i = 0; i < source.Count; i++) { Wv = 1; // Обнуляем и выставляем начальное значение весов if (i >= Period) { // Вычисляем произведение весов на цену // Относительно текущего i-го элемента нужно слева направо обработать // свечки, для этого необходимо вычислить границы расчетов относительно i for(int k = (i-Period+1); k <= i; k++) { wma1 = wma1 + (Wv * source[k]); Wv++; } vWMA = wma1/W; } else { vWMA = source[i]; } //nWMA[i] = wma1/W; // Вычисляем значение WMA в i-ой точке nWMA.Add(vWMA); // Вычисляем значение WMA в i-ой точке wma1 = 0; // Обнуляем промежуточное значение суммы, чтобы не было накопления } return nWMA; } //================================================================================ // Параметры оптимизации // параметра для длинных позиций public OptimProperty LongPeriod1Param = new OptimProperty(25, 10, 50, 5); public OptimProperty LongPeriod2Param = new OptimProperty(40, 20, 100, 10); public OptimProperty LongATRPeriodParam = new OptimProperty(26, 10, 40, 2); public OptimProperty LongStopKoefParam = new OptimProperty(4, 1, 10, 0.5); // параметра для коротких позиций public OptimProperty ShortPeriod1Param = new OptimProperty(25, 10, 50, 5); public OptimProperty ShortPeriod2Param = new OptimProperty(80, 20, 100, 10); public OptimProperty ShortATRPeriodParam = new OptimProperty(12, 10, 40, 2); public OptimProperty ShortStopKoefParam = new OptimProperty(4, 1, 10, 0.5); //================================================================================ public virtual void Execute(IContext ctx, ISecurity source) { int StartBar = 0; #region Variables // для длинных позиций // период расчета WMA int LongPeriod1; // для быстрой WMA int LongPeriod2; // для медленной WMA int LongATRPeriod; // для ATR int LongStopKoef; // коэффициент стоп-лосс к ATR bool bLongCrossOver; // флаг пересечения снизу вверх bool bLongCrossUnder; // флаг пересечения сверху вниз double LongStopPrice; // цена стоп-лосс // для коротких позиций // период расчета WMA int ShortPeriod1; // для быстрой WMA int ShortPeriod2; // для медленной WMA int ShortATRPeriod; // для ATR int ShortStopKoef; // коэффициент стоп-лосс к ATR bool bShortCrossOver; // флаг пересечения снизу вверх bool bShortCrossUnder; // флаг пересечения сверху вниз double ShortStopPrice; // цена стоп-лосс #endregion //-------------------------------------------------------------------------------- #region Obtain parameters LongPeriod1 = LongPeriod1Param; LongPeriod2 = LongPeriod2Param; LongATRPeriod = LongATRPeriodParam; LongStopKoef = LongStopKoefParam; ShortPeriod1 = ShortPeriod1Param; ShortPeriod2 = ShortPeriod2Param; ShortATRPeriod = ShortATRPeriodParam; ShortStopKoef = ShortStopKoefParam; StartBar = LongPeriod1; if (LongPeriod2 > StartBar) StartBar = LongPeriod2; if (LongATRPeriod > StartBar) StartBar = LongATRPeriod; if (ShortPeriod1 > StartBar) StartBar = ShortPeriod1; if (ShortPeriod2 > StartBar) StartBar = ShortPeriod2; if (ShortATRPeriod > StartBar) StartBar = ShortATRPeriod; // Вычисляем верхнюю и нижнюю границы // Используем GetData для кеширования данных и ускорения оптимизации. // для длинных позиций // серия для быстрой взвешенной скользящей средней WMA IList nLongWMA1 = ctx.GetData("LongWMA1", new[] {LongPeriod1.ToString()}, delegate { return GenWMA(source.ClosePrices, LongPeriod1); }); // серия для медленной взвешенной скользящей средней WMA IList nLongWMA2 = ctx.GetData("LongWMA2", new[] {LongPeriod2.ToString()}, delegate { return GenWMA(source.ClosePrices, LongPeriod2); }); // серия для ATR IList nLongATR = ctx.GetData("LongATR", new[] {LongATRPeriod.ToString()}, delegate { return Series.AverageTrueRange(source.Bars, LongATRPeriod); }); // для коротких позиций // серия для быстрой взвешенной скользящей средней WMA IList nShortWMA1 = ctx.GetData("ShortWMA1", new[] {ShortPeriod1.ToString()}, delegate { return GenWMA(source.ClosePrices, ShortPeriod1); }); // серия для медленной взвешенной скользящей средней WMA IList nShortWMA2 = ctx.GetData("ShortWMA2", new[] {ShortPeriod2.ToString()}, delegate { return GenWMA(source.ClosePrices, ShortPeriod2); }); // серия для ATR IList nShortATR = ctx.GetData("ShortATR", new[] {ShortATRPeriod.ToString()}, delegate { return Series.AverageTrueRange(source.Bars, ShortATRPeriod); }); #endregion //================================================================================ #region основной цикл - проход по барам int barsCount = source.Bars.Count; for (int bar = StartBar; bar < barsCount-1; bar++) { //-------------------------------------------------------------------------------- #region calculate values bLongCrossOver = false; bLongCrossUnder = false; bShortCrossOver = false; bShortCrossUnder = false; if ((nLongWMA1[bar] > nLongWMA2[bar]) && (nLongWMA1[bar-1] <= nLongWMA2[bar-1])) bLongCrossOver = true; if ((nLongWMA1[bar] < nLongWMA2[bar]) && (nLongWMA1[bar-1] >= nLongWMA2[bar-1])) bLongCrossUnder = true; if ((nShortWMA1[bar] > nShortWMA2[bar]) && (nShortWMA1[bar-1] <= nShortWMA2[bar-1])) bShortCrossOver = true; if ((nShortWMA1[bar] < nShortWMA2[bar]) && (nShortWMA1[bar-1] >= nShortWMA2[bar-1])) bShortCrossUnder = true; #endregion //-------------------------------------------------------------------------------- #region generate signals // сброс значений сигналов bBuy = false; bSell = false; bShort = false; bCover = false; LongStopPrice = 0; ShortStopPrice = 0; // установка сигналов по условиям // для длинных позиций if (bLongCrossOver) bBuy = true; if (bLongCrossUnder) bSell = true; if (LongPos != null) { LongStopPrice = LongPos.EntryPrice - LongStopKoef * nLongATR[bar]; } // для коротких позиций if (bShortCrossUnder) bShort = true; if (bShortCrossOver) bCover = true; if (ShortPos != null) { ShortStopPrice = ShortPos.EntryPrice + ShortStopKoef * nShortATR[bar]; } #endregion //================================================================================ #region execute signals //-------------------------------------------------------------------------------- // выполнение сигналов для длинной позиции LongPos = source.Positions.GetLastActiveForSignal("LN"); if (LongPos == null) { // Если нет активной длинной позиции if (bBuy) { // Если есть сигнал Buy, // выдаем ордер на открыте новой длинной позиции. source.Positions.BuyAtMarket(bar+1, 1, "LN"); } } else { // Если есть активная длинная позиция LongPos.CloseAtStop(bar+1, LongStopPrice, "LX"); if (LongPos.IsActive && bSell) { // Если есть сигнал Sell, // выдаем ордер на закрыте длинной позиции. LongPos.CloseAtMarket(bar+1, "LX"); } } //-------------------------------------------------------------------------------- // выполнение сигналов для короткой позиции ShortPos = source.Positions.GetLastActiveForSignal("SN"); if (ShortPos == null) { // Если нет активной короткой позиции if (bShort) { // Если есть сигнал Short // выдаем ордер на открыте новой короткой позиции. source.Positions.SellAtMarket(bar+1, 1, "SN"); } } else { // Если есть активная короткая позиция, ShortPos.CloseAtStop(bar+1, ShortStopPrice, "SX"); if (ShortPos.IsActive && bCover) { // Если есть сигнал Cover // выдаем ордер на закрыте короткой позиции. ShortPos.CloseAtMarket(bar+1, "SX"); } } #endregion } #endregion //================================================================================ #region прорисовка графиков // Берем основную панель (Pane) IPane mainPane = ctx.First; // для длинных позиций // Отрисовка скользящих средних mainPane.AddList("LongWMA1", nLongWMA1, ListStyles.LINE, 0x00a0a0, LineStyles.DOT, PaneSides.RIGHT); mainPane.AddList("LongWMA2", nLongWMA2, ListStyles.LINE, 0x007070, LineStyles.DOT, PaneSides.RIGHT); // для коротких позиций // Отрисовка скользящих средних mainPane.AddList("ShortWMA1", nShortWMA1, ListStyles.LINE, 0xa000a0, LineStyles.DOT, PaneSides.RIGHT); mainPane.AddList("ShortWMA2", nShortWMA2, ListStyles.LINE, 0x700070, LineStyles.DOT, PaneSides.RIGHT); // Создаем дополнительную панель для индикатора ATR. IPane ATRPane = ctx.CreatePane("ATR", 10, false, false); // Отрисовка графика индикатора ATRPane.AddList(string.Format("LongATR"), nLongATR, ListStyles.LINE, 0x5050f0, LineStyles.SOLID, PaneSides.RIGHT); ATRPane.AddList(string.Format("ShortATR"), nShortATR, ListStyles.LINE, 0xf05050, LineStyles.SOLID, PaneSides.RIGHT); #endregion //-------------------------------------------------------------------------------- } } }