#include "sierrachart.h" /*==========================================================================*/ SCSFExport scsf_TradingSystemBasedOnAlertCondition(SCStudyInterfaceRef sc) { SCInputRef Input_Enabled = sc.Input[0]; SCInputRef Input_NumBarsToCalculate = sc.Input[1]; SCInputRef Input_DrawStyleOffsetType = sc.Input[2]; SCInputRef Input_PercentageOrTicksOffset = sc.Input[3]; SCInputRef Input_OrderActionOnAlert = sc.Input[4]; SCInputRef Input_EvaluateOnBarCloseOnly = sc.Input[5]; SCInputRef Input_SendTradeOrdersToTradeService = sc.Input[6]; SCInputRef Input_MaximumPositionAllowed = sc.Input[7]; SCInputRef Input_MaximumLongTradesPerDay = sc.Input[8]; SCInputRef Input_MaximumShortTradesPerDay = sc.Input[9]; SCInputRef Input_EnableDebuggingOutput = sc.Input[10]; SCInputRef Input_CancelAllWorkingOrdersOnExit = sc.Input[11]; SCInputRef Input_AllowTradingOnlyDuringTimeRange = sc.Input[12]; SCInputRef Input_StartTimeForAllowedTimeRange = sc.Input[13]; SCInputRef Input_EndTimeForAllowedTimeRange = sc.Input[14]; SCInputRef Input_AllowOnlyOneTradePerBar = sc.Input[15]; SCInputRef Input_Version = sc.Input[16]; SCInputRef Input_SupportReversals = sc.Input[17]; SCInputRef Input_AllowMultipleEntriesInSameDirection = sc.Input[18]; SCInputRef Input_AllowEntryWithWorkingOrders = sc.Input[19]; SCInputRef Input_ControlBarButtonNumberForEnableDisable = sc.Input[20]; SCSubgraphRef BuyEntry = sc.Subgraph[0]; SCSubgraphRef SellEntry = sc.Subgraph[1]; if (sc.SetDefaults) { // Set the configuration and defaults sc.GraphName = "Trading System Based on Alert Condition"; sc.StudyDescription = ""; sc.AutoLoop = 0; // manual looping sc.GraphRegion = 0; sc.CalculationPrecedence = LOW_PREC_LEVEL; Input_Enabled.Name = "Enabled"; Input_Enabled.SetYesNo(false); Input_NumBarsToCalculate.Name = "Number of Bars to Calculate"; Input_NumBarsToCalculate.SetInt(2000); Input_NumBarsToCalculate.SetIntLimits(1, MAX_STUDY_LENGTH); //Input_DrawStyleOffsetType.Name = "Draw Style Offset Type"; //Input_DrawStyleOffsetType.SetCustomInputStrings("Percentage;Number of Ticks"); //Input_PercentageOrTicksOffset.Name = "Percentage or Ticks Offset"; //Input_PercentageOrTicksOffset.SetFloat(0); Input_OrderActionOnAlert.Name = "Order Action on Alert"; Input_OrderActionOnAlert.SetCustomInputStrings("Buy Entry;Buy Exit;Sell Entry;Sell Exit"); Input_OrderActionOnAlert.SetCustomInputIndex(0); Input_EvaluateOnBarCloseOnly.Name = "Evaluate on Bar Close Only"; Input_EvaluateOnBarCloseOnly.SetYesNo(true); Input_SendTradeOrdersToTradeService.Name = "Send Trade Orders to Trade Service"; Input_SendTradeOrdersToTradeService.SetYesNo(false); Input_MaximumPositionAllowed.Name = "Maximum Position Allowed"; Input_MaximumPositionAllowed.SetInt(1); Input_MaximumLongTradesPerDay.Name = "Maximum Long Trades Per Day"; Input_MaximumLongTradesPerDay.SetInt(2); Input_MaximumShortTradesPerDay.Name = "Maximum Short Trades Per Day"; Input_MaximumShortTradesPerDay.SetInt(2); Input_CancelAllWorkingOrdersOnExit.Name = "Cancel All Working Orders On Exit"; Input_CancelAllWorkingOrdersOnExit.SetYesNo(false); Input_EnableDebuggingOutput.Name = "Enable Debugging Output"; Input_EnableDebuggingOutput.SetYesNo(false); Input_AllowTradingOnlyDuringTimeRange.Name = "Allow Trading Only During Time Range"; Input_AllowTradingOnlyDuringTimeRange.SetYesNo(false); Input_StartTimeForAllowedTimeRange.Name = "Start Time For Allowed Time Range"; Input_StartTimeForAllowedTimeRange.SetTime(HMS_TIME(0, 0, 0)); Input_EndTimeForAllowedTimeRange.Name = "End Time For Allowed Time Range"; Input_EndTimeForAllowedTimeRange.SetTime(HMS_TIME(23, 59, 59)); Input_AllowOnlyOneTradePerBar.Name = "Allow Only One Trade per Bar"; Input_AllowOnlyOneTradePerBar.SetYesNo(true); Input_SupportReversals.Name = "Support Reversals"; Input_SupportReversals.SetYesNo(false); Input_AllowMultipleEntriesInSameDirection.Name = "Allow Multiple Entries In Same Direction"; Input_AllowMultipleEntriesInSameDirection.SetYesNo(false); Input_AllowEntryWithWorkingOrders.Name = "Allow Entry With Working Orders"; Input_AllowEntryWithWorkingOrders.SetYesNo(false); Input_ControlBarButtonNumberForEnableDisable.Name = "ACS Control Bar Button # for Enable/Disable"; Input_ControlBarButtonNumberForEnableDisable.SetInt(1); Input_ControlBarButtonNumberForEnableDisable.SetIntLimits(1, MAX_ACS_BUTTONS); Input_Version.SetInt(2); BuyEntry.Name = "Buy"; BuyEntry.DrawStyle = DRAWSTYLE_POINT_ON_HIGH; BuyEntry.LineWidth = 5; SellEntry.Name = "Sell"; SellEntry.DrawStyle = DRAWSTYLE_POINT_ON_LOW; SellEntry.LineWidth = 5; sc.AllowMultipleEntriesInSameDirection = false; sc.SupportReversals = false; sc.AllowOppositeEntryWithOpposingPositionOrOrders = false; sc.SupportAttachedOrdersForTrading = false; sc.UseGUIAttachedOrderSetting = true; sc.CancelAllOrdersOnEntriesAndReversals = false; sc.AllowEntryWithWorkingOrders = false; // Only 1 trade for each Order Action type is allowed per bar. sc.AllowOnlyOneTradePerBar = true; //This should be set to true when a trading study uses trading functions. sc.MaintainTradeStatisticsAndTradesData = true; return; } if (Input_Version.GetInt() < 2) { Input_AllowOnlyOneTradePerBar.SetYesNo(true); Input_Version.SetInt(2); } sc.AllowOnlyOneTradePerBar = Input_AllowOnlyOneTradePerBar.GetYesNo(); sc.SupportReversals = false; sc.MaximumPositionAllowed = Input_MaximumPositionAllowed.GetInt(); sc.SendOrdersToTradeService = Input_SendTradeOrdersToTradeService.GetYesNo(); sc.CancelAllWorkingOrdersOnExit = Input_CancelAllWorkingOrdersOnExit.GetYesNo(); sc.SupportReversals = Input_SupportReversals.GetYesNo(); sc.AllowMultipleEntriesInSameDirection = Input_AllowMultipleEntriesInSameDirection.GetYesNo(); sc.AllowEntryWithWorkingOrders = Input_AllowEntryWithWorkingOrders.GetYesNo(); if (sc.MenuEventID == Input_ControlBarButtonNumberForEnableDisable.GetInt()) { const int ButtonState = (sc.PointerEventType == SC_ACS_BUTTON_ON) ? 1 : 0; Input_Enabled.SetYesNo(ButtonState); } if (!Input_Enabled.GetYesNo() || sc.LastCallToFunction) return; int& r_LastDebugMessageType = sc.GetPersistentInt(1); int& r_PriorFormulaState = sc.GetPersistentInt(2); if (sc.IsFullRecalculation) { r_LastDebugMessageType = 0; r_PriorFormulaState = 0; // set ACS Tool Control Bar tool tip sc.SetCustomStudyControlBarButtonHoverText(Input_ControlBarButtonNumberForEnableDisable.GetInt(), "Enable/Disable Trade System Based on Alert Condition"); sc.SetCustomStudyControlBarButtonEnable(Input_ControlBarButtonNumberForEnableDisable.GetInt(), Input_Enabled.GetBoolean()); } // Figure out the last bar to evaluate int LastIndex = sc.ArraySize - 1; if (Input_EvaluateOnBarCloseOnly.GetYesNo()) --LastIndex; const int EarliestIndexToCalculate = LastIndex - Input_NumBarsToCalculate.GetInt() + 1; int CalculationStartIndex = sc.GetCalculationStartIndexForStudy(); if (CalculationStartIndex < EarliestIndexToCalculate) CalculationStartIndex = EarliestIndexToCalculate; sc.EarliestUpdateSubgraphDataArrayIndex = CalculationStartIndex; s_SCPositionData PositionData; s_ACSTradeStatistics TradeStatistics; SCString TradeNotAllowedReason; bool IsTradeAllowed = !sc.IsFullRecalculation && !sc.DownloadingHistoricalData; if (Input_EnableDebuggingOutput.GetYesNo()) { if (sc.IsFullRecalculation) TradeNotAllowedReason = "Is full recalculation"; else if (sc.DownloadingHistoricalData) TradeNotAllowedReason = "Downloading historical data"; } if (Input_AllowTradingOnlyDuringTimeRange.GetYesNo()) { const SCDateTime LastBarDateTime = sc.LatestDateTimeForLastBar; const int LastBarTimeInSeconds = LastBarDateTime.GetTimeInSeconds(); bool LastIndexIsInAllowedTimeRange = false; if (Input_StartTimeForAllowedTimeRange.GetTime() <= Input_EndTimeForAllowedTimeRange.GetTime()) { LastIndexIsInAllowedTimeRange = (LastBarTimeInSeconds >= Input_StartTimeForAllowedTimeRange.GetTime() && LastBarTimeInSeconds <= Input_EndTimeForAllowedTimeRange.GetTime() ); } else //Reversed times. { LastIndexIsInAllowedTimeRange = (LastBarTimeInSeconds >= Input_StartTimeForAllowedTimeRange.GetTime() || LastBarTimeInSeconds <= Input_EndTimeForAllowedTimeRange.GetTime() ); } if (!LastIndexIsInAllowedTimeRange) { IsTradeAllowed = false; TradeNotAllowedReason = "Not within allowed time range"; } } bool ParseAndSetFormula = sc.IsFullRecalculation; // Evaluate each of the bars for (int BarIndex = CalculationStartIndex; BarIndex <= LastIndex; ++BarIndex) { int FormulaResult = sc.EvaluateAlertConditionFormulaAsBoolean(BarIndex, ParseAndSetFormula); ParseAndSetFormula = false; bool StateHasChanged = true; if (FormulaResult == r_PriorFormulaState ) { TradeNotAllowedReason = "Formula state has not changed"; StateHasChanged = false; } r_PriorFormulaState = FormulaResult; // Determine the result if (FormulaResult != 0) //Formula is true { if (IsTradeAllowed) { sc.GetTradePosition(PositionData); sc.GetTradeStatisticsForSymbol(0, 1, TradeStatistics); } if (Input_OrderActionOnAlert.GetIndex() == 0)// Buy entry { BuyEntry[BarIndex] = 1; SellEntry[BarIndex] = 0; if (IsTradeAllowed && StateHasChanged) { if (PositionData.PositionQuantityWithAllWorkingOrders == 0 || Input_AllowMultipleEntriesInSameDirection.GetYesNo() || Input_SupportReversals.GetYesNo()) { if (TradeStatistics.TotalLongTrades < Input_MaximumLongTradesPerDay.GetInt()) { s_SCNewOrder NewOrder; NewOrder.OrderQuantity = sc.TradeWindowOrderQuantity; NewOrder.OrderType = SCT_ORDERTYPE_MARKET; int Result = (int)sc.BuyEntry(NewOrder); if (Result < 0) sc.AddMessageToTradeServiceLog(sc.GetTradingErrorTextMessage(Result), false, true); } else if (Input_EnableDebuggingOutput.GetYesNo()) { sc.AddMessageToTradeServiceLog("Buy Entry signal ignored. Maximum Long Trades reached.", false, true); } } else if (Input_EnableDebuggingOutput.GetYesNo()) { sc.AddMessageToTradeServiceLog("Buy Entry signal ignored. Position exists.", false, true); } } else if (Input_EnableDebuggingOutput.GetYesNo() && r_LastDebugMessageType != 1) { SCString MessageText("Trading is not allowed. Reason: "); MessageText += TradeNotAllowedReason; sc.AddMessageToTradeServiceLog(MessageText, 0); r_LastDebugMessageType = 1; } } else if (Input_OrderActionOnAlert.GetIndex() == 1)// Buy exit { BuyEntry[BarIndex] = 0; SellEntry[BarIndex] = 1; if (IsTradeAllowed && StateHasChanged) { if (PositionData.PositionQuantity > 0) { s_SCNewOrder NewOrder; NewOrder.OrderQuantity = sc.TradeWindowOrderQuantity; NewOrder.OrderType = SCT_ORDERTYPE_MARKET; int Result = (int)sc.BuyExit(NewOrder); if (Result < 0) sc.AddMessageToTradeServiceLog(sc.GetTradingErrorTextMessage(Result), false, true); } else if (Input_EnableDebuggingOutput.GetYesNo()) { sc.AddMessageToTradeServiceLog("Buy Exit signal ignored. Long position does not exist.", false, true); } } else if (Input_EnableDebuggingOutput.GetYesNo() && r_LastDebugMessageType != 1) { SCString MessageText("Trading is not allowed. Reason: "); MessageText += TradeNotAllowedReason; sc.AddMessageToTradeServiceLog(MessageText, 0); r_LastDebugMessageType = 1; } } else if(Input_OrderActionOnAlert.GetIndex() == 2)// Sell entry { BuyEntry[BarIndex] = 0; SellEntry[BarIndex] = 1; if (IsTradeAllowed && StateHasChanged) { if (PositionData.PositionQuantityWithAllWorkingOrders == 0 || Input_AllowMultipleEntriesInSameDirection.GetYesNo() || Input_SupportReversals.GetYesNo()) { if (TradeStatistics.TotalShortTrades < Input_MaximumShortTradesPerDay.GetInt()) { s_SCNewOrder NewOrder; NewOrder.OrderQuantity = sc.TradeWindowOrderQuantity; NewOrder.OrderType = SCT_ORDERTYPE_MARKET; int Result = (int)sc.SellEntry(NewOrder); if (Result < 0) sc.AddMessageToTradeServiceLog(sc.GetTradingErrorTextMessage(Result), false, true); } else if (Input_EnableDebuggingOutput.GetYesNo()) { sc.AddMessageToTradeServiceLog("Sell Entry signal ignored. Maximum Short Trades reached.", false, true); } } else if (Input_EnableDebuggingOutput.GetYesNo()) { sc.AddMessageToTradeServiceLog("Sell Entry signal ignored. Position exists.", false, true); } } else if (Input_EnableDebuggingOutput.GetYesNo() && r_LastDebugMessageType != 1) { SCString MessageText("Trading is not allowed. Reason: "); MessageText += TradeNotAllowedReason; sc.AddMessageToTradeServiceLog(MessageText, 0); r_LastDebugMessageType = 1; } } else if (Input_OrderActionOnAlert.GetIndex() == 3)// Sell exit { BuyEntry[BarIndex] = 1; SellEntry[BarIndex] = 0; if (IsTradeAllowed && StateHasChanged) { if (PositionData.PositionQuantity < 0) { s_SCNewOrder NewOrder; NewOrder.OrderQuantity = sc.TradeWindowOrderQuantity; NewOrder.OrderType = SCT_ORDERTYPE_MARKET; int Result = (int)sc.SellExit(NewOrder); if (Result < 0) sc.AddMessageToTradeServiceLog(sc.GetTradingErrorTextMessage(Result), false, true); } else if (Input_EnableDebuggingOutput.GetYesNo()) { sc.AddMessageToTradeServiceLog("Sell Exit signal ignored. Short position does not exist.", false, true); } } else if (Input_EnableDebuggingOutput.GetYesNo() && r_LastDebugMessageType != 1) { SCString MessageText("Trading is not allowed. Reason: "); MessageText += TradeNotAllowedReason; sc.AddMessageToTradeServiceLog(MessageText, 0); r_LastDebugMessageType = 1; } } } else { BuyEntry[BarIndex] = 0; SellEntry[BarIndex] = 0; } // Reset the prior formula state to 0 if we did not allow trading in order to not prevent trading the next time it is allowed if the state did not change. if (!IsTradeAllowed) r_PriorFormulaState = 0; } if (sc.IsFullRecalculation) r_LastDebugMessageType = 0; }