    #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];
        SCInputRef Input_CancelAllOrdersOnEntries = sc.Input[21];
        SCInputRef Input_CancelAllOrdersOnReversals = sc.Input[22];

        SCSubgraphRef Subgraph_BuyEntry = sc.Subgraph[0];
        SCSubgraphRef Subgraph_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;Flatten");
            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_CONTROL_BAR_BUTTONS);

            Input_CancelAllOrdersOnEntries.Name = "Cancel All Orders on Entries";
            Input_CancelAllOrdersOnEntries.SetYesNo(false);

            Input_CancelAllOrdersOnReversals.Name = "Cancel All Orders on Reversals";
            Input_CancelAllOrdersOnReversals.SetYesNo(false);

            Input_Version.SetInt(2);

            Subgraph_BuyEntry.Name = "Buy";
            Subgraph_BuyEntry.DrawStyle = DRAWSTYLE_POINT_ON_HIGH;
            Subgraph_BuyEntry.LineWidth = 5;

            Subgraph_SellEntry.Name = "Sell";
            Subgraph_SellEntry.DrawStyle = DRAWSTYLE_POINT_ON_LOW;
            Subgraph_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.CancelAllOrdersOnEntries = Input_CancelAllOrdersOnEntries.GetYesNo();
        sc.CancelAllOrdersOnReversals = Input_CancelAllOrdersOnReversals.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.UpdateStartIndex;// sc.GetCalculationStartIndexForStudy();

        if (CalculationStartIndex < EarliestIndexToCalculate)
            CalculationStartIndex = EarliestIndexToCalculate;

        sc.EarliestUpdateSubgraphDataArrayIndex = CalculationStartIndex;

        s_SCPositionData PositionData;

        n_ACSIL::s_TradeStatistics TradeStatistics;

        SCString TradeNotAllowedReason;

        const bool IsDownloadingHistoricalData = sc.ChartIsDownloadingHistoricalData(sc.ChartNumber) != 0;

        bool IsTradeAllowed = !sc.IsFullRecalculation && !IsDownloadingHistoricalData;

        if (Input_EnableDebuggingOutput.GetYesNo())
        {
            if (sc.IsFullRecalculation)
                TradeNotAllowedReason = "Is full recalculation";
            else if (IsDownloadingHistoricalData)
                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;
        SCString DateTimeString;

        // Evaluate each of the bars
        for (int BarIndex = CalculationStartIndex; BarIndex <= LastIndex; ++BarIndex)
        {
            if (Input_EnableDebuggingOutput.GetYesNo())
            {
                DateTimeString = ". BarDate-Time: ";
                DateTimeString += sc.DateTimeToString(sc.BaseDateTimeIn[BarIndex], FLAG_DT_COMPLETE_DATETIME_US);
            }

            int FormulaResult = sc.EvaluateAlertConditionFormulaAsBoolean(BarIndex, ParseAndSetFormula);
            ParseAndSetFormula = false;

            bool StateHasChanged = true;
            if (FormulaResult == r_PriorFormulaState)
            {
                TradeNotAllowedReason = "Formula state has not changed. Current state: ";
                TradeNotAllowedReason += FormulaResult ? "True" : "False";
                StateHasChanged = false;
            }

            r_PriorFormulaState = FormulaResult;

            // Determine the result
            if (FormulaResult != 0) //Formula is true
            {
                if (IsTradeAllowed)
                {
                    sc.GetTradePosition(PositionData);
                    sc.GetTradeStatisticsForSymbolV2(n_ACSIL::STATS_TYPE_DAILY_ALL_TRADES, TradeStatistics);
                }

                if (Input_OrderActionOnAlert.GetIndex() == 0)// Buy entry
                {
                    Subgraph_BuyEntry[BarIndex] = 1;
                    Subgraph_SellEntry[BarIndex] = 0;

                    if (IsTradeAllowed && StateHasChanged)
                    {
                        if (PositionData.PositionQuantityWithAllWorkingOrders == 0
                            || Input_AllowMultipleEntriesInSameDirection.GetYesNo()
                            || Input_SupportReversals.GetYesNo())
                        {
                            if (TradeStatistics.LongTrades < Input_MaximumLongTradesPerDay.GetInt())
                            {
                                s_SCNewOrder NewOrder;
                                NewOrder.OrderQuantity = sc.TradeWindowOrderQuantity;
                                NewOrder.OrderType = SCT_ORDERTYPE_MARKET;

                                int Result = static_cast<int>(sc.BuyEntry(NewOrder, BarIndex));
                                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;
                        MessageText += DateTimeString;
                        sc.AddMessageToTradeServiceLog(MessageText, 0);
                        r_LastDebugMessageType = 1;
                    }
                }
                else if (Input_OrderActionOnAlert.GetIndex() == 1)// Buy exit
                {
                    Subgraph_BuyEntry[BarIndex] = 0;
                    Subgraph_SellEntry[BarIndex] = 1;

                    if (IsTradeAllowed && StateHasChanged)
                    {
                        if (PositionData.PositionQuantity > 0)
                        {
                            s_SCNewOrder NewOrder;
                            NewOrder.OrderQuantity = sc.TradeWindowOrderQuantity;
                            NewOrder.OrderType = SCT_ORDERTYPE_MARKET;
                            int Result = static_cast<int>(sc.BuyExit(NewOrder, BarIndex));
                            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
                            r_PriorFormulaState = 0;
                    }
                    else if (Input_EnableDebuggingOutput.GetYesNo() && r_LastDebugMessageType != 1)
                    {
                        SCString MessageText("Trading is not allowed. Reason: ");
                        MessageText += TradeNotAllowedReason;
                        MessageText += DateTimeString;
                        sc.AddMessageToTradeServiceLog(MessageText, 0);
                        r_LastDebugMessageType = 1;
                    }
                }
                else if(Input_OrderActionOnAlert.GetIndex() == 2)// Sell entry
                {
                    Subgraph_BuyEntry[BarIndex] = 0;
                    Subgraph_SellEntry[BarIndex] = 1;

                    if (IsTradeAllowed  && StateHasChanged)
                    {
                        if (PositionData.PositionQuantityWithAllWorkingOrders == 0
                            || Input_AllowMultipleEntriesInSameDirection.GetYesNo()
                            || Input_SupportReversals.GetYesNo())
                        {
                            if (TradeStatistics.ShortTrades < Input_MaximumShortTradesPerDay.GetInt())
                            {
                                s_SCNewOrder NewOrder;
                                NewOrder.OrderQuantity = sc.TradeWindowOrderQuantity;
                                NewOrder.OrderType = SCT_ORDERTYPE_MARKET;
                                int Result = static_cast<int>(sc.SellEntry(NewOrder, BarIndex));
                                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;
                        MessageText += DateTimeString;
                        sc.AddMessageToTradeServiceLog(MessageText, 0);
                        r_LastDebugMessageType = 1;
                    }
                }
                else if (Input_OrderActionOnAlert.GetIndex() == 3)// Sell exit
                {
                    Subgraph_BuyEntry[BarIndex] = 1;
                    Subgraph_SellEntry[BarIndex] = 0;

                    if (IsTradeAllowed && StateHasChanged)
                    {
                        if (PositionData.PositionQuantity < 0)
                        {
                            s_SCNewOrder NewOrder;
                            NewOrder.OrderQuantity = sc.TradeWindowOrderQuantity;
                            NewOrder.OrderType = SCT_ORDERTYPE_MARKET;
                            int Result = static_cast<int>(sc.SellExit(NewOrder, BarIndex));
                            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
                            r_PriorFormulaState = 0;
                    }
                    else if (Input_EnableDebuggingOutput.GetYesNo() && r_LastDebugMessageType != 1)
                    {
                        SCString MessageText("Trading is not allowed. Reason: ");
                        MessageText += TradeNotAllowedReason;
                        MessageText += DateTimeString;
                        sc.AddMessageToTradeServiceLog(MessageText, 0);
                        r_LastDebugMessageType = 1;
                    }
                }
                else if (Input_OrderActionOnAlert.GetIndex() == 4)// Flatten
                {
                    if (IsTradeAllowed && StateHasChanged)
                    {
                        int Result = sc.FlattenAndCancelAllOrders();
                        if (Result < 0)
                        {
                            sc.AddMessageToTradeServiceLog(sc.GetTradingErrorTextMessage(Result), false, true);
                        }
                    }
                    else if (Input_EnableDebuggingOutput.GetYesNo() && r_LastDebugMessageType != 3)
                    {
                        SCString MessageText("Trading is not allowed. Reason: ");
                        MessageText += TradeNotAllowedReason;
                        MessageText += DateTimeString;
                        sc.AddMessageToTradeServiceLog(MessageText, 0);
                        r_LastDebugMessageType = 3;
                    }
                }

            }
            else// Formula is not true.
            {
                Subgraph_BuyEntry[BarIndex] = 0;
                Subgraph_SellEntry[BarIndex] = 0;

                if (Input_EnableDebuggingOutput.GetYesNo() && r_LastDebugMessageType != 2)
                {
                    SCString MessageText("Formula is not true");

                    if (!TradeNotAllowedReason.IsEmpty())
                    {
                        MessageText += ". ";
                        MessageText += TradeNotAllowedReason;
                    }

                    MessageText += DateTimeString;
                    sc.AddMessageToTradeServiceLog(MessageText, 0);
                    r_LastDebugMessageType = 2;
                }
            }

            //  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;
    }