public void AddList(string name, Security security, CandleStyles listSlyle, SystemColor color, PaneSides side) { var colorTSlab = new TsLabColor(color.ToArgb()); var securityTSLab = (SecurityTSlab)security; pane.AddList(name, securityTSLab.security, listSlyle, colorTSlab, side); }
public void Execute(IContext ctx, ISecurity sec) { if (Display.Value.ToString().Equals(DisplayGraph.Display.ToString())) { IGraphPane pane = ctx.First ?? ctx.CreateGraphPane("First", "First"); Color colorCandle = ScriptColors.Black; var graphSec = pane.AddList(sec.Symbol, sec, CandleStyles.BAR_CANDLE, colorCandle, PaneSides.RIGHT); } }
public void Execute(IContext ctx, ISecurity sec) { // таймер для определения времени выполнения скрипта var sw = Stopwatch.StartNew(); // проверка таймфрейма, используемого для торговли if (sec.IntervalInstance != new Interval(30, DataIntervals.MINUTE)) { throw new InvalidOperationException($"Выбран не корректный интервал для торговли{sec.IntervalInstance}. Работать только на таймфрейме 30 минут!"); } #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)); // свечи дневного таймфрейма var daySec = sec.CompressTo(new Interval(1, DataIntervals.DAYS)); // канал Дончиана для дневного таймфрейма IList <double> upChannelDay = ctx.GetData("UpChannelDay", new string[] { PeriodChannelDay.ToString() }, () => { var res = Series.Highest(daySec.HighPrices, PeriodChannelDay); return(daySec.Decompress(res)); }); IList <double> downChannelDay = ctx.GetData("DownChannelDay", new string[] { PeriodChannelDay.ToString() }, () => { var res = Series.Lowest(daySec.LowPrices, PeriodChannelDay); return(daySec.Decompress(res)); }); #endregion #region первый и последний бар для расчетов // первый бар, используемый для расчетов int startBar = Math.Max(PeriodChannel + 1, ctx.TradeFromBar); // значение счетчика баров, используемое для расчетов int barsCount = ctx.BarsCount; if (!ctx.IsLastBarClosed) { barsCount--; } #endregion // цены закрытия, используемые для торговли var closePrices = sec.ClosePrices; // вначале трейл-стоп отключен, он будет включен при открытии добавочной позиции LED var onTrailStop = false; var trailEnable = false; var lastTrailPrice = 0.0; #region Торговый цикл for (int i = startBar; i < barsCount; i++) { // базовая позиция LEB var lebPosition = sec.Positions.GetLastActiveForSignal("LEB", i); // добавочная позиция LEA, открывается при получении заданного уровня профита по базовой позиции var leaPosition = sec.Positions.GetLastActiveForSignal("LEA", i); // суммарно базовая и добавочная позиции - позиции, начинающиеся на "LA" var lePositions = sec.Positions.GetActiveForBar(i).Where(p => p.EntrySignalName.StartsWith("LE")).ToList(); // добавочная позиция LED при выходе цены за дневной канал var ledPosition = sec.Positions.GetLastActiveForSignal("LED", i); // если нет базовой позиции, то выставляем условную заявку на её открытие if (lebPosition == null) { sec.Positions.BuyIfGreater(i + 1, 1, upChannel[i], Slippage * sec.Tick, "LEB"); onTrailStop = false; trailEnable = false; lastTrailPrice = 0.0; OnStopLoss.Value = true; } else { // если есть базовая позиция LEB, нет добавочной позиции LEA и нет добавочной позиции LED // то выставляем условную заявку на открытие добавочной позиции LEA if (leaPosition == null && ledPosition == null) { // цена входа в добавочную позицию double entryPrice = lebPosition.EntryPrice * (1 + AddPositionLevel / 100.0); sec.Positions.BuyIfGreater(i + 1, 1, entryPrice, Slippage, "LEA"); } // если нет добавочной позиции LED и цена пробила дневной канал, то открываем добавочную позицию LED if (ledPosition == null && (closePrices[i - 1] <= upChannelDay[i - 1]) && (closePrices[i] > upChannelDay[i])) { sec.Positions.BuyAtMarket(i + 1, 2, "LED"); OnStopLoss.Value = false; onTrailStop = true; } // выставляем стоп-лосс и тейк-профит для всех открытых позиций по средней цене входа, // вычисляем среднюю цену входа в позицию var avrEntryPrice = lePositions.GetAvgEntryPrice(); // стоп-лосc foreach (var pos in lePositions) { if (OnStopLoss || OnBreakevenStop || onTrailStop) { // цена стоп-лосса double stopPrice = (OnStopLoss) ? (avrEntryPrice * (1 - StopLossPct / 100.0)) : 0; // цена инструмента для перевода позиции в безубыток double movePosBreakevenPrice = avrEntryPrice * (1 + ProfitForBreakevenPct / 100.0); // цена стопа для перевода позиции в безубыток double breakevenStopPrice = (OnBreakevenStop && closePrices[i] > movePosBreakevenPrice) ? avrEntryPrice : 0; // цена трейл-стопа double trailPrice = 0.0; if (onTrailStop) { double onTrailStopPrice = avrEntryPrice * (1 + TrailStopEnablePct / 100.0); if (closePrices[i] > onTrailStopPrice) { trailEnable = true; } trailPrice = (trailEnable) ? sec.HighPrices[i] * (1 - TrailStopPct / 100.0) : avrEntryPrice * (1 - StopLossPct / 100.0); trailPrice = lastTrailPrice = Math.Max(trailPrice, lastTrailPrice); } // берем максимальное значение стопа-лосса stopPrice = Math.Max(stopPrice, breakevenStopPrice); stopPrice = Math.Max(stopPrice, trailPrice); stopPrice = Math.Max(stopPrice, pos.GetStop(i)); if (stopPrice != 0) { pos.CloseAtStop(i + 1, stopPrice, Slippage, "LXS"); } } } // тейк-профит if (OnTakeProfit) { double profitPrice = avrEntryPrice * (1 + TakeProfitPct / 100.0); lePositions.ForEach(p => p.CloseAtProfit(i + 1, profitPrice, Slippage, "LXP")); } } } #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.Blue, LineStyles.SOLID, PaneSides.RIGHT); var lineDownChannel = pane.AddList(string.Format($"DwChannel ({PeriodChannel,0}"), downChannel, ListStyles.LINE, ScriptColors.Blue, LineStyles.SOLID, PaneSides.RIGHT); var lineUpChannelDay = pane.AddList(string.Format($"UpChannelDay ({PeriodChannelDay,0}"), upChannelDay, ListStyles.LINE, ScriptColors.Red, LineStyles.DASH, PaneSides.RIGHT); lineUpChannelDay.Thickness = 2; var lineDownChannelDay = pane.AddList(string.Format($"DwChannelDay ({PeriodChannelDay,0}"), downChannelDay, ListStyles.LINE, ScriptColors.Red, LineStyles.DASH, PaneSides.RIGHT); lineDownChannelDay.Thickness = 2; #endregion #region 'Вывод в лог' // Вывод в лог времени выполнения скрипта только в режиме Лаборатория if (!ctx.Runtime.IsAgentMode) { ctx.Log($"Скрипт выполнен за время: {sw.Elapsed}", MessageType.Info, true); } #endregion }
/// <summary> /// Вывод графиков в TSLab /// </summary> private void DrawGraph() { // Если идет процесс оптимизации, то графики не выводим if (_ctx.IsOptimization) { return; } IGraphPane pane1 = _ctx.First ?? _ctx.CreateGraphPane("First", "First"); Color colorCandle = ScriptColors.Black; var graphSec1 = pane1.AddList(_sec1.Symbol, _sec1, CandleStyles.BAR_CANDLE, colorCandle, PaneSides.RIGHT); IGraphPane pane2 = _ctx.CreateGraphPane("Second", "Second"); Color colorCandle2 = ScriptColors.Black; var graphSec2 = pane2.AddList(_sec2.Symbol, _sec2, CandleStyles.BAR_CANDLE, colorCandle, PaneSides.RIGHT); IGraphPane pane3 = _ctx.CreateGraphPane("Delta", "Delta"); Color colorCandle3 = ScriptColors.Black; var graphSpred = pane3.AddList("Spred", _delta, ListStyles.HISTOHRAM, ScriptColors.Blue, LineStyles.SOLID, PaneSides.RIGHT); var graphUpChannel = pane3.AddList($"UpChannel", _upChannel, ListStyles.LINE, ScriptColors.Red, LineStyles.SOLID, PaneSides.RIGHT); var graphDownChannel = pane3.AddList($"DownChannel", _downChannel, ListStyles.LINE, ScriptColors.Red, LineStyles.SOLID, PaneSides.RIGHT); /* * var graphSignalLE = pane.AddList("SignalLE", * _arrSignalLE, * ListStyles.HISTOHRAM, * ScriptColors.Green, * LineStyles.SOLID, * PaneSides.LEFT); * * var graphignalSE = pane.AddList("SignalSE", * _arrSignalSE, * ListStyles.HISTOHRAM, * ScriptColors.Red, * LineStyles.SOLID, * PaneSides.LEFT); * * * // раскрашиваем бары в зависимости от наличия позиции * _arrVolume = new double[_barsCount]; * for (int i = 0; i < _barsCount; i++) * { * var activePositions1 = _sec1.Positions.GetActiveForBar(i).ToList(); * graphSec1.SetColor(i, activePositions1.Any() ? ScriptColors.Black : ScriptColors.DarkGray); * var activePositions2 = _sec1.Positions.GetActiveForBar(i).ToList(); * graphSec2.SetColor(i, activePositions2.Any() ? ScriptColors.Black : ScriptColors.DarkGray); * } * /* * IGraphPane pane4 = _ctx.CreateGraphPane("Four", "Four"); * var graphVolume = pane4.AddList("Volume", * _arrVolume, * ListStyles.HISTOHRAM_LINE, * ScriptColors.Blue, * LineStyles.SOLID, * PaneSides.RIGHT); */ }
public virtual void Execute(IContext context, ISecurity symbol) // IContext ctx - источник данных, ISecurity sec - фин инструмент и инф.о нем { #region Забираем значения из параметров double punktPriceRub = _punktPriceRub.Value; // 2d / 100 * 66; //стоимость пункта по фьючу на РТС double maxKontraktSize = _maxKontraktSize.Value; //Указываем максимальное количество контрактов. Защита "От Дурака" на момент отладки double maxPercentRisk = _maxPercentRisk.Value; //максимальный риск в одной сделке, double absComission = _absComission.Value; int CandlesForHighFractal = _CandlesForHighFractal.Value; //_periodExit.ValueInt; //Определяем период канала int CandlesForLowFractal = _CandlesForLowFractal.Value; //_periodExit.ValueInt; //Определяем период канала bool writeToLog = _writeToLog.Value; //по умолчанию true #endregion #region Переменные для работы с Агентом double extraBalanceMoney = 0; //для режима "Агент" сколько денег временно вывели со счёта double countOfTradingSystems = 10; //количество одновременно торгуемых систем double currentBalanceForAgent = 0; //Переменная для определения суммы на счёте в ТСЛаб #endregion #region забираем данные из xml - файла string pathXML_TSLab = @"D:\Лаборатория Трейдинга - VisualStudio\Файл конфигурации\configXML.xml"; // string pathXML_TSLab = @"C:\TSLab2 - Tools\Файл конфигурации XML\configXML.xml"; XmlDocument doc = new XmlDocument(); //экземпляр класса для работы с xml документами doc.Load(pathXML_TSLab); //загружаем созданный xml - документ foreach (XmlNode node in doc.DocumentElement) { string name = node.Attributes[0].Value; if (name == "ALOR_TradingLab") //указываем тот счёт на котором должна вестись торговля { extraBalanceMoney = double.Parse(node["extraBalanceMoney"].InnerText); countOfTradingSystems = double.Parse(node["countOfTradingSystems"].InnerText); break; } } #endregion #region определяем процент на одну торговую систему double pctForSystem = 100.0 / countOfTradingSystems; //указываем максимальный процент денег на одну систему double maxPctForOneSystem = 10.0; //указываем максимальный процент на одну торговую систему pctForSystem = Math.Min(maxPctForOneSystem, pctForSystem); pctForSystem = Math.Round(pctForSystem, 2); #endregion #region Переменные для торговой системы int firstValidValue = 0; // Первое значение свечки при которой существуют все индикаторы double FinResForBar = 0; //Переменная, в которой указывается фин. результат на текущий бар double moneyForTradingSystem = 0; //Переменная, которая будет хранить в себе оценку портфеля bool signalBuy = false; //Сигнал на вход в длинную позицию double orderEntryLong = 0; // Цена, где будет расположен вход в длинную позицию double stopPriceLong = 0; // Цена где будет расположен StopLoss длинной позиции double kontraktShort_mPR = 0; bool signalShort = false; // Сигналы на вход в короткую позиции double orderEntryShort = 0; //Цена, где будет расположен вод в короткую позицию double stopPriceShort = 0; // Цена где будет расположен StopLoss длинной позиции double kontraktBuy_mPR = 0; double exitLimitPrice = 0; //Цена выставления Лимитированной заявки на выход string HandlerName = String.Format(""); //текстовая переменная для названия индикатора string ErrorText = String.Format(""); //текстовая переменная для вывода текста ошибки #endregion #region Проводим расчёт Абсолютной комиссии (AbsComission) - не индикатор, а проводим расчёт: HandlerName = String.Format("Абсолютная комиссия по {0}", symbol.CacheName); ErrorText = String.Format("Ошибка при вычислении блока {0}. Индекс за пределами диапазона.", HandlerName); //Устанавливаем размер комиссии AbsComission_h.Commission = absComission; //устанавливаем комиссию в рублях на контракт AbsComission_h.Execute(symbol); //Показываем, что эта комиссия должна учитываться для фин. инструмента #endregion ISecurityRt rtSymbol = symbol as ISecurityRt;// создаем объект для доступа к информации реальной торговли #region Создаём удобное обращение к ценам (по аналогии с Wealth-Lab) IList <double> Close = symbol.GetClosePrices(context); IList <double> Open = symbol.GetOpenPrices(context); IList <double> High = symbol.GetHighPrices(context);//получаем список максимальных цен IList <double> Low = symbol.GetLowPrices(context); #endregion #region Индикаторы #region Верхние фракталы FractalBuyDouble_h.Context = context; FractalBuyDouble_h.CurrentBar = 0; //появляется когда фрактал полностью сформируется FractalBuyDouble_h.Fractal = 0; FractalBuyDouble_h.Left = CandlesForHighFractal; FractalBuyDouble_h.Right = CandlesForHighFractal; HandlerName = String.Format("Верхний фрактал (свечей влево: {0}, свечей вправо: {1}) по инструменту: {2}", CandlesForHighFractal, CandlesForHighFractal, symbol.CacheName); ErrorText = String.Format("Ошибка при вычислении блока {0}. Индекс за пределами диапазона.", HandlerName); IList <double> highFractal = context.GetData (HandlerName, //вводим название нового индикатора new[] { HandlerName }, //работа с буфером данных delegate // именно здесь расчитывается индикатор { try { return(FractalBuyDouble_h.Execute(symbol)); } //Рассчитываем индикатор catch (ArgumentOutOfRangeException) //если произошла ошибка { throw new ScriptException(ErrorText); } //выводим текст ошибки } ); firstValidValue = Math.Max(firstValidValue, CandlesForHighFractal * 2 + 1); #endregion #region Нижние фракталы FractalSellDouble_h.Context = context; FractalSellDouble_h.CurrentBar = 0; //появляется когда фрактал полностью сформируется FractalSellDouble_h.Fractal = 0; FractalSellDouble_h.Left = CandlesForLowFractal; FractalSellDouble_h.Right = CandlesForLowFractal; HandlerName = String.Format("Нижний фрактал (свечей влево: {0}, свечей вправо: {1}) по инструменту: {2}", CandlesForLowFractal, CandlesForLowFractal, symbol.CacheName); ErrorText = String.Format("Ошибка при вычислении блока {0}. Индекс за пределами диапазона.", HandlerName); IList <double> lowFractal = context.GetData (HandlerName, //вводим название нового индикатора new[] { HandlerName }, //работа с буфером данных delegate // именно здесь расчитывается индикатор { try { return(FractalSellDouble_h.Execute(symbol)); } //Рассчитываем индикатор catch (ArgumentOutOfRangeException) //если произошла ошибка { throw new ScriptException(ErrorText); } //выводим текст ошибки } ); firstValidValue = Math.Max(firstValidValue, CandlesForHighFractal * 2 + 1); #endregion #endregion #region Пишем сообщение в лог о начале срабатывания метода Execute() - если значение writeLog == true if (writeToLog == true) { if (symbol.IsRealtime) { string logText = String.Format ("Привет! Я ТСЛаб. Запускаю стратегию в Режиме Агента. ExtraMoney={0}; торгуется систем: {1}; % для системы = {2}", extraBalanceMoney.ToString(), countOfTradingSystems, pctForSystem); context.Log(logText, new Color(0, 0, 0), true); //выводим сообщение в лог } else { string logText = String.Format ("Привет! Я ТСЛаб. Я начала тестировать стратегию в Режиме Лаборатории. ExtraMoney={0}; торгуется систем: {1}; % для системы = {2}", extraBalanceMoney, countOfTradingSystems, pctForSystem); context.Log(logText, new Color(0, 0, 0), true); //выводим сообщение в лог } } #endregion #region Главный торговый цикл for (int bar = firstValidValue; bar < symbol.Bars.Count; bar++) { this.LastActivePosition = symbol.Positions.GetLastPositionActive(bar);// получить ссылку на последнию позицию #region Определяем цены лимитных заявок: orderEntryLong = Close[bar]; orderEntryLong = symbol.RoundPrice(orderEntryLong); orderEntryShort = Close[bar]; orderEntryShort = symbol.RoundPrice(orderEntryShort); exitLimitPrice = Close[bar]; exitLimitPrice = symbol.RoundPrice(exitLimitPrice); #endregion #region Условия на вход в позицию signalBuy = Close[bar] > highFractal[bar] && highFractal[bar] > lowFractal[bar]; signalShort = Close[bar] < lowFractal[bar] && highFractal[bar] > lowFractal[bar]; #endregion #region Сопровождение и выход из позиции #region если позиция существует if (LastActivePosition != null) // Если позиция есть { #region Длинная позиция if (LastActivePosition.IsLong == true) { bool ExitLong = false; ExitLong = Close[bar] < lowFractal[bar]; //signalShort; #region нужно выходить на следующем баре if (ExitLong == true && signalBuy == false) { LastActivePosition.CloseAtPrice(bar + 1, exitLimitPrice, "Exit Long"); } #endregion } #endregion #region Короткая позиция else if (LastActivePosition.IsShort) { bool ExitShort = false; ExitShort = Close[bar] > highFractal[bar]; //signalBuy; #region нужно выходить на следующем баре if (ExitShort == true && signalShort == false) { LastActivePosition.CloseAtPrice(bar + 1, exitLimitPrice, "Exit Short"); } #endregion } #endregion } #endregion #region если позиция отсутствует else // Если нет позиции { #region Нужно входить в длинную позицию? if (signalBuy) // Пришёл сигнал в длинную позицию { stopPriceLong = lowFractal[bar]; //Math.Min(valueLow_1, valueLow_2); //устанавливаем стоп-лос stopPriceLong = symbol.RoundPrice(stopPriceLong); //округляем цену до минимального тика #region Определяем кол-во контрактов на покупку (в зависимости от режима торговли - Лаборатория или Агент: #region Находимся в режиме Агента? if (symbol.IsRealtime) //если находимся в режиме агента { //определяем текущий баланс счёта (В ТСЛаб) currentBalanceForAgent = rtSymbol.EstimatedBalance; moneyForTradingSystem = (currentBalanceForAgent + extraBalanceMoney) * (pctForSystem / 100.0); lots_maxPercentRisk_h.LotSize = symbol.LotSize; //устанавливаем размер лота lots_maxPercentRisk_h.MaxPercentRisk = maxPercentRisk; //указываем процент риска который используем lots_maxPercentRisk_h.punktPriceRUB = punktPriceRub; //указываем стоимость пункта kontraktBuy_mPR = lots_maxPercentRisk_h.Execute(moneyForTradingSystem, orderEntryLong, stopPriceLong, bar); kontraktBuy_mPR = symbol.RoundShares(kontraktBuy_mPR); kontraktBuy_mPR = Math.Min(kontraktBuy_mPR, maxKontraktSize); //ограничиваем максимальное кол-во контрактов на вход (не более ... контрактов) #region пишем сообщение в лог: if (bar > symbol.Bars.Count - 2) { string logTextBuy = String.Format( "Хочу войти в long: {0} контрактами: Баланс на счёте: {1}; ExtraMoney: {2} руб.; %для системы: {3}, Деньги для системы: {4}", kontraktBuy_mPR, currentBalanceForAgent, extraBalanceMoney, pctForSystem, moneyForTradingSystem); context.Log(logTextBuy, MessageType.Info, true); //выводим сообщение в лог } #endregion } #endregion #region Находимся в режиме Лаборатории? else //если находимся в режиме лаборатории { string logTextBuy = ""; WholeTimeProfit_h.ProfitKind = ProfitKind.Unfixed; //если хотим узнать стоимость счёта с учётом ещё незафиксированной прибыли // WholeTimeProfit_h.ProfitKind = ProfitKind.Fixed; //если хотим узнать стоимость счёта без учёта ещё незафиксированной прибыли FinResForBar = WholeTimeProfit_h.Execute(symbol, bar); //Рассчитываем стоимость дохода на текущий бар в пунктах FinResForBar = FinResForBar * punktPriceRub; //переводим финрез в рубли // определяем сумму для торговой системы moneyForTradingSystem = symbol.InitDeposit + FinResForBar; lots_maxPercentRisk_h.LotSize = symbol.LotSize; //устанавливаем размер лота lots_maxPercentRisk_h.MaxPercentRisk = maxPercentRisk; //указываем процент риска который используем lots_maxPercentRisk_h.punktPriceRUB = punktPriceRub; //указываем стоимость пункта kontraktBuy_mPR = lots_maxPercentRisk_h.Execute(moneyForTradingSystem, orderEntryLong, stopPriceLong, bar); kontraktBuy_mPR = symbol.RoundShares(kontraktBuy_mPR); #region пишем сообщение в лог (если не в режиме оптимизации): if (writeToLog == true) { if (context.IsOptimization == false) //если не в режиме оптимизации { logTextBuy = String.Format( "Цена входа: {0} - Stop: {1} Результат по закрытым позициям: {2}; Начальный депозит: {3} руб.; Деньги для системы: {4}. бар №{5} - Хочу войти в long: {6} контрактами: ", orderEntryLong, stopPriceLong, FinResForBar, symbol.InitDeposit, moneyForTradingSystem, bar, kontraktBuy_mPR); context.Log(logTextBuy, new Color(0, 0, 0), false); //выводим сообщение в лог } } #endregion } #endregion #endregion #region Входим не менее чем на 1 контракт if (kontraktBuy_mPR > 0) { // kontraktBuy_mPR = 1; string orderText = String.Format("LongEnter"); symbol.Positions.BuyAtPrice(bar + 1, kontraktBuy_mPR, orderEntryLong, orderText); } else { kontraktBuy_mPR = 1; //даже если не хватает денег - заходим одним контрактом string orderText = String.Format("LongEnter_minKontrakt"); symbol.Positions.BuyAtPrice(bar + 1, 1, kontraktBuy_mPR, orderText); // входим хотя бы 1 контрактом } #endregion } #endregion #region Нужно входить в короткую позицию? else if (signalShort) { // Пришёл сигнал в короткую позицию stopPriceShort = highFractal[bar]; //Math.Max(valueHigh_1, valueHigh_2); //Устанавливаем Стоп (для расчёта кол-ва контрактов) #region Определяем кол-во контрактов на продажу (в зависимости от режима торговли - Лаборатория или Агент: #region Если находимся в режиме агента if (symbol.IsRealtime) //если находимся в режиме агента { //определяем текущий баланс счёта (В ТСЛаб) currentBalanceForAgent = rtSymbol.EstimatedBalance; moneyForTradingSystem = (currentBalanceForAgent + extraBalanceMoney) * (pctForSystem / 100.0); lots_maxPercentRisk_h.LotSize = symbol.LotSize; //устанавливаем размер лота lots_maxPercentRisk_h.MaxPercentRisk = maxPercentRisk; //указываем процент риска который используем kontraktShort_mPR = lots_maxPercentRisk_h.Execute(moneyForTradingSystem, orderEntryShort, stopPriceShort, bar); kontraktShort_mPR = symbol.RoundShares(kontraktShort_mPR); // Защита от дурака (от непредвиденных событий) - только в режими агента kontraktShort_mPR = Math.Min(kontraktShort_mPR, maxKontraktSize); //ограничиваем максимальное кол-во контрактов на вход (не более ... контрактов) #region пишем сообщение в лог: if (bar > symbol.Bars.Count - 2) { string logTextShort = String.Format( "Хочу войти в short: {0} контрактами: Баланс на счёте: {1}; ExtraMoney: {2} руб.; %для системы: {3}, Деньги для системы: {4}", kontraktShort_mPR, currentBalanceForAgent, extraBalanceMoney, pctForSystem, moneyForTradingSystem); context.Log(logTextShort, MessageType.Info, true); //выводим сообщение в лог } #endregion } #endregion #region Если находимся в режиме Лаборатории else //если находимся в режиме лаборатории { //определяем прибыль от торговли на текущий момент и показываем, что хотим учесть даже ещё незафиксированную прибыль WholeTimeProfit_h.ProfitKind = ProfitKind.Unfixed; //если хотим узнать стоимость счёта с учётом ещё незафиксированной прибыли FinResForBar = WholeTimeProfit_h.Execute(symbol, bar); //Рассчитываем доход по портфелю на текущий бар в пунктах FinResForBar = FinResForBar * punktPriceRub; //переводим Фин. Рез. в рубли // определяем сумму для торговой системы moneyForTradingSystem = symbol.InitDeposit + FinResForBar; //расчет с помощью "кубика" lots_maxPercentRisk_h.LotSize = symbol.LotSize; //устанавливаем размер лота lots_maxPercentRisk_h.MaxPercentRisk = maxPercentRisk; //указываем процент риска который используем lots_maxPercentRisk_h.punktPriceRUB = punktPriceRub; kontraktShort_mPR = lots_maxPercentRisk_h.Execute(moneyForTradingSystem, orderEntryShort, stopPriceShort, bar); kontraktShort_mPR = symbol.RoundShares(kontraktShort_mPR); #region пишем сообщение в лог (если не в режиме оптимизации): if (writeToLog == true) { if (context.IsOptimization == false) { string logTextShort = ""; logTextShort = String.Format( "Цена входа: {0} - Stop: {1} Результат по закрытым позициям: {2}; Начальный депозит: {3} руб.; Деньги для системы: {4}. бар №{5} - Хочу войти в short: {6} контрактами: ", orderEntryShort, stopPriceShort, FinResForBar, symbol.InitDeposit, moneyForTradingSystem, bar, kontraktShort_mPR); context.Log(logTextShort, new Color(0, 0, 0), false); //выводим сообщение в лог } } #endregion } #endregion #endregion #region Входим не менее чем на 1 контракт if (kontraktShort_mPR > 0) { // kontraktShort_mPR = 1; string orderText = String.Format("ShortEnter"); symbol.Positions.SellAtPrice(bar + 1, kontraktShort_mPR, orderEntryShort, orderText); } else { kontraktShort_mPR = 1; //даже если не хватает денег - заходим одним контрактом string orderText = String.Format("ShortEnter_minKontrakt"); symbol.Positions.SellAtPrice(bar + 1, kontraktShort_mPR, orderEntryShort, orderText); } #endregion } #endregion } #endregion #endregion } #endregion #region Прорисовка графиков if (context.IsOptimization == false) // Если находимся не в режиме оптимизации, то пропускаем отрисовку { #region Пишем сообщение в лог о завершении стратегии if (writeToLog == true) { if (symbol.IsRealtime) { string logText = String.Format("Режима агента. Осталось только раскрасить график"); context.Log(logText, new Color(0, 0, 0), true); //выводим сообщение в лог } else { string logText = String.Format("Режим Лаборатории: Осталось только раскрасить график!"); context.Log(logText, new Color(0, 0, 0), true); //выводим сообщение в лог } } #endregion #region Панель для цен и индикаторов (по ценам) #region Создаём панель для цен: IGraphPane pricePane = context.CreateGraphPane(symbol.ToString(), null); pricePane.Visible = true; //является ли панель видимой? pricePane.HideLegend = false; //скрыть (true) или показать (false) легенду pricePane.SizePct = 100; //сколько % составляет #endregion #region создаём переменные для цветов Color LightGray = 0xc0c0c0; //Серый цвет Color Blue = 0x0000ff; //Синий цвет Color redColor = new Color(255, 0, 0); //создаём красный цвет c помощью RGB (берём значение в Яндексе по запросу "красный цвет код") Color greenColor = new Color(50, 205, 50); //создаём зелёный цвет #endregion #region Заносим бары на панель графика IGraphList candlesGraph = pricePane.AddList(symbol.ToString(), symbol, CandleStyles.BAR_CANDLE, LightGray, PaneSides.RIGHT); symbol.ConnectSecurityList(candlesGraph); //подключить график к ценной бумаге для обновления в режиме реального времени candlesGraph.AlternativeColor = LightGray; //для чисел (к отрицательным) для баров (к медвежьим) candlesGraph.Autoscaling = true; pricePane.UpdatePrecision(TSLab.Script.PaneSides.RIGHT, symbol.Decimals); //обновить значение на графике #endregion //Рисуем индикаторы #region Свойства индикаторов string highLevelText = String.Format("Верхняя фрактальная граница канала (слева: {0}, справа: {1})", CandlesForHighFractal, CandlesForHighFractal); string lowLevelText = String.Format("Нижняя фрактальная граница канала (слева: {0}, справа: {1})", CandlesForLowFractal, CandlesForLowFractal); // string lowTrailingText = String.Format("LongTrailingStop (период канала: {0}, Количество шагов: {1})", periodExit, steps); // string highTrailingText = String.Format("ShortTrailingStop (период канала: {0}, Количество шагов: {1})", periodExit, steps); LineStyles highLevelLineStyle = LineStyles.SOLID; //устанавливаем тип линии (по умолчанию Dot) для нижнего канала LineStyles lowLevelLineStyle = LineStyles.SOLID; //устанавливаем тип линии (по умолчанию Dot) для нижнего канала // LineStyles lowTrailingLineStyle = LineStyles.DOT; //устанавливаем тип линии (по умолчанию Dot) для нижнего канала // LineStyles highTrailingLineStyle = LineStyles.DOT; //устанавливаем тип линии (по умолчанию Dot) для нижнего канала ListStyles highLevelListStyle = ListStyles.POINT; //Устанавливаем форму линии (по умолчанию Line) для нижнего канала ListStyles lowLevelListStyle = ListStyles.POINT; //Устанавливаем форму линии (по умолчанию Line) для нижнего канала // ListStyles lowTrailingListStyle = ListStyles.LINE_WO_ZERO; //Устанавливаем форму линии (по умолчанию Line) для нижнего канала // ListStyles highTrailingListStyle = ListStyles.LINE_WO_ZERO; //Устанавливаем форму линии (по умолчанию Line) для нижнего канала #endregion #region Наносим индикаторы на график IGraphListBase lowLevelGraph = pricePane.AddList("lowFractalLevel", lowLevelText, lowFractal, lowLevelListStyle, redColor, lowLevelLineStyle, PaneSides.RIGHT); lowLevelGraph.Thickness = 2; //устанавливаем толщину отображаемой линии lowLevelGraph.AlternativeColor = redColor; IGraphListBase highLevelGraph = pricePane.AddList("highFractalLevel", highLevelText, highFractal, highLevelListStyle, greenColor, highLevelLineStyle, PaneSides.RIGHT); highLevelGraph.Thickness = 1; //устанавливаем толщину отображаемой линии highLevelGraph.AlternativeColor = greenColor; //IGraphListBase lowTrailingGraph = pricePane.AddList("TralingForLong", lowTrailingText, TrailingForLong, lowTrailingListStyle, redColor, lowTrailingLineStyle, PaneSides.RIGHT); //lowTrailingGraph.Thickness = 2; //устанавливаем толщину отображаемой линии //lowTrailingGraph.AlternativeColor = redColor; //IGraphListBase highTrailingGraph = pricePane.AddList("TrailingForShort", highTrailingText, TrailingForShort, highTrailingListStyle, redColor, highTrailingLineStyle, PaneSides.RIGHT); //highTrailingGraph.Thickness = 2; //устанавливаем толщину отображаемой линии //highTrailingGraph.AlternativeColor = redColor; #endregion #endregion #region асскрашиваем свечи в цвета в зависимости от наличия и типа позиции for (int i = 0; i < context.BarsCount; i++) { //Расскрашиваем свечи в цвета, если в позиции var ActivePositionsList = symbol.Positions.GetActiveForBar(i); if (ActivePositionsList.Any()) //если есть хотя бы одна позиция { if (ActivePositionsList.First().IsLong) //ElementAt(0).IsLong) { candlesGraph.SetColor(i, greenColor); //Зелёный цвет если в длинной позиции } else { candlesGraph.SetColor(i, redColor); //Красный цвет - если в короткой позиции } } else { // lst.SetColor(i, LightGray); //Серый цвет если позиции нет } } #endregion } #endregion #region Пишем сообщение в лог о завершении стратегии if (writeToLog == true) { if (symbol.IsRealtime) { string logText = String.Format("Режима агента. Стратегия выполнена. Жду дальнейших указаний!"); context.Log(logText, new Color(0, 0, 0), true); //выводим сообщение в лог } else { string logText = String.Format("Режим Лаборатории: Стратегия выполнена - можно отдохнуть!"); context.Log(logText, new Color(0, 0, 0), true); //выводим сообщение в лог } } #endregion }
/// <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 }
/// <summary> /// Вывод графиков в TSLab /// </summary> private void DrawGraph() { // Если идет процесс оптимизации, то графики не выводим if (_ctx.IsOptimization) { return; } IGraphPane pane = _ctx.First ?? _ctx.CreateGraphPane("First", "First"); Color colorCandle = ScriptColors.Black; var graphSec = pane.AddList(_sec.Symbol, _sec, CandleStyles.BAR_CANDLE, colorCandle, PaneSides.RIGHT); var graphFastAlligator = pane.AddList($"fastAlligator ({AlligatorFastLenght}-{AlligatorFastShift})", _fastAlligator, ListStyles.LINE, ScriptColors.Red, LineStyles.SOLID, PaneSides.RIGHT); var graphMiddleAlligator = pane.AddList($"middleAlligator ({AlligatorMiddleLenght}-{AlligatorMiddleShift})", _middleAlligator, ListStyles.LINE, ScriptColors.Green, LineStyles.SOLID, PaneSides.RIGHT); var graphSlowAlligator = pane.AddList($"slowAlligator ({AlligatorSlowLenght}-{AlligatorSlowShift})", _slowAlligator, ListStyles.LINE, ScriptColors.Blue, LineStyles.SOLID, PaneSides.RIGHT); /* * var graphSignalLE = pane.AddList("SignalLE", * _arrSignalLE, * ListStyles.HISTOHRAM, * ScriptColors.Green, * LineStyles.SOLID, * PaneSides.LEFT); * * var graphignalSE = pane.AddList("SignalSE", * _arrSignalSE, * ListStyles.HISTOHRAM, * ScriptColors.Red, * LineStyles.SOLID, * PaneSides.LEFT); * /* * var graphSignalLX = pane.AddList("SignalLX", * _arrSignalLX, * ListStyles.HISTOHRAM, * ScriptColors.Yellow, * LineStyles.SOLID, * PaneSides.LEFT); * * var graphignalSX = pane.AddList("SignalSX", * _arrSignalSX, * ListStyles.HISTOHRAM, * ScriptColors.Magenta, * LineStyles.SOLID, * PaneSides.LEFT); * * /* * // раскрашиваем бары в зависимости от наличия позиции * // и определяем объем входа в позицию на каждом баре * _arrVolume = new double[_barsCount]; * for (int i = 0; i < _barsCount; i++) * { * var activePositions = _sec.Positions.GetActiveForBar(i).ToList(); * graphSec.SetColor(i, activePositions.Any() ? ScriptColors.Black : ScriptColors.DarkGray); * * foreach (var pos in activePositions) * { * if (pos.EntryBar.Date.Date == _sec.Bars[i].Date.Date && * pos.EntryBar.Date.TimeOfDay == _sec.Bars[i].Date.TimeOfDay) * _arrVolume[i] += pos.Shares; * } * } * * IGraphPane pane2 = _ctx.CreateGraphPane("Second", "Second"); * var graphVolume = pane2.AddList("Volume", * _arrVolume, * ListStyles.HISTOHRAM_LINE, * ScriptColors.Blue, * LineStyles.SOLID, * PaneSides.RIGHT); */ }
public virtual void Execute(IContext ctx, ISecurity source) { double[] pyData = new double[DataLen]; // Array of data we want to send to Python App var indicatorValues = new List <double>(); for (int i = 0; i < DataLen; i++) { // Fill first `DataLen` elements for indicator and full python array pyData[i] = source.ClosePrices[i]; indicatorValues.Add(0); } //-------------------------------------------------------------------------------- #region Trading cycle int barsCount = source.Bars.Count; int lastEntryBar = 0; for (int bar = DataLen; bar < barsCount; bar++) { //Create new array. Copy DataLen -1 elements from old array and insert new datapoint. // We always have DataLen last points of data double[] newData = new double[DataLen]; Array.Copy(pyData, 1, newData, 0, DataLen - 1); newData[DataLen - 1] = source.ClosePrices[bar]; pyData = newData; var val = GetFromPY(pyData); // Send to python and get calculated value // Insert calculated value if it's not NaN if (double.IsNaN(val)) { indicatorValues.Add(0); } else { indicatorValues.Add(val); } // Some dummy trade logic IPosition LongPos = source.Positions.GetLastActiveForSignal("LN", bar); if (LongPos == null) { if (val < LongEntryThreshold) { source.Positions.BuyAtPrice(bar + 1, 1, source.ClosePrices[bar], "LN"); lastEntryBar = bar + 1; } } else { if (val > ShortEntryThreshold) { LongPos.CloseAtPrice(bar + 1, source.ClosePrices[bar], "LX"); } } } #endregion //-------------------------------------------------------------------------------- #region Draw charts IGraphPane mainPane = ctx.CreateGraphPane("Main", ""); //Candlestick IGraphList CandleChart = mainPane.AddList( "Candle Chart", string.Format("Symbol: [{0}]", source.Symbol), source, CandleStyles.BAR_CANDLE, CandleFillStyle.Decreasing, true, -14685179, PaneSides.RIGHT ); source.ConnectSecurityList(CandleChart); CandleChart.AlternativeColor = -262137; CandleChart.Autoscaling = true; mainPane.UpdatePrecision(PaneSides.RIGHT, source.Decimals); //Python indicator IGraphList ind = mainPane.AddList( "Indicator", indicatorValues, ListStyles.LINE, 0xa000a0, LineStyles.SOLID, PaneSides.RIGHT); mainPane.Visible = true; mainPane.HideLegend = false; mainPane.SizePct = 100; ind.AlternativeColor = -6153042; ind.Autoscaling = true; mainPane.UpdatePrecision(PaneSides.RIGHT, source.Decimals); #endregion }
/// <summary> /// Вывод графиков в TSLab /// </summary> private void DrawGraph() { // Если идет процесс оптимизации, то графики не выводим if (_ctx.IsOptimization) { return; } IGraphPane pane = _ctx.First ?? _ctx.CreateGraphPane("First", "First"); Color colorCandle = ScriptColors.Black; var graphSec = pane.AddList(_sec.Symbol, _sec, CandleStyles.BAR_CANDLE, colorCandle, PaneSides.RIGHT); var graphUpBollinger = pane.AddList($"upBollinger ({PeriodBollinger} - {DeviationBollinger})", _upBollinger, ListStyles.LINE, ScriptColors.Blue, LineStyles.SOLID, PaneSides.RIGHT); var graphDownBollinger = pane.AddList($"downBollinger ({PeriodBollinger} - {DeviationBollinger})", _downBollinger, ListStyles.LINE, ScriptColors.Blue, LineStyles.SOLID, PaneSides.RIGHT); if (MethodOutOfPosition.Value == 2) { var graphCenterBollinger = pane.AddList("centerBollinger", _centerBollinger, ListStyles.LINE, ScriptColors.Blue, LineStyles.SOLID, PaneSides.RIGHT); } var graphSignalLE = pane.AddList("SignalLE", _arrSignalLE, ListStyles.HISTOHRAM, ScriptColors.Green, LineStyles.SOLID, PaneSides.LEFT); var graphignalSE = pane.AddList("SignalSE", _arrSignalSE, ListStyles.HISTOHRAM, ScriptColors.Red, LineStyles.SOLID, PaneSides.LEFT); // раскрашиваем бары в зависимости от наличия позиции // и определяем объем входа в позицию на каждом баре _arrVolume = new double[_barsCount]; for (int i = 0; i < _barsCount; i++) { var activePositions = _sec.Positions.GetActiveForBar(i).ToList(); graphSec.SetColor(i, activePositions.Any() ? ScriptColors.Black : ScriptColors.DarkGray); foreach (var pos in activePositions) { if (pos.EntryBar.Date.Date == _sec.Bars[i].Date.Date && pos.EntryBar.Date.TimeOfDay == _sec.Bars[i].Date.TimeOfDay) { _arrVolume[i] += pos.Shares; } } } IGraphPane pane2 = _ctx.CreateGraphPane("Second", "Second"); var graphVolume = pane2.AddList("Volume", _arrVolume, ListStyles.HISTOHRAM_LINE, ScriptColors.Blue, LineStyles.SOLID, PaneSides.RIGHT); }