    #include "sierrachart.h"

    SCSFExport scsf_TradeManagementByStudy(SCStudyInterfaceRef sc)
    {
        // Setup References
        SCInputRef Input_Enabled = sc.Input[0];
        SCInputRef Input_OrderType = sc.Input[1];
        SCInputRef Input_ControlSubgraphRef = sc.Input[2];
        SCInputRef Input_AdjustmentTiming = sc.Input[3];
        SCInputRef Input_AdjustmentFrequency = sc.Input[4];
        SCInputRef Input_TrailingOffset = sc.Input[5];
        SCInputRef Input_ACSButtonNumber = sc.Input[6];
        SCInputRef Input_PositionType = sc.Input[7];
        SCInputRef Input_DetailedLogging = sc.Input[8];
        SCInputRef Input_OnlyModifyInOneDirection = sc.Input[9];
        SCInputRef Input_Version = sc.Input[10];
        SCInputRef Input_AllowZeroValues = sc.Input[11];
        SCInputRef Input_AllowNegativeValues= sc.Input[12];
        SCInputRef Input_AllowCustomPropertiesForControlBarButton = sc.Input[13];
        SCInputRef Input_AttachedOrderToManage = sc.Input[14];
        SCInputRef Input_ChangeToOffAllOtherCustomStudyButtonsOnEnable = sc.Input[15];

        if (sc.SetDefaults)
        {
            sc.GraphName = "Trade Management by Study";
            sc.AutoLoop = 0;//We are using Manual looping for efficiency since historical data is not relevant with this study
            sc.GraphRegion = 0;
            sc.CalculationPrecedence = VERY_LOW_PREC_LEVEL;

            Input_Enabled.Name = "Enabled";
            Input_Enabled.SetYesNo(0);

            Input_OrderType.Name = "Order Type To Manage";
            Input_OrderType.SetCustomInputStrings("Stops;Targets");
            Input_OrderType.SetCustomInputIndex(0);

            Input_ControlSubgraphRef.Name = "Controlling Subgraph Reference";
            Input_ControlSubgraphRef.SetStudySubgraphValues(0, 0);

            Input_AdjustmentTiming.Name = "Order Adjustment Timing";
            Input_AdjustmentTiming.SetCustomInputStrings("Every N Seconds;On Bar Close");
            Input_AdjustmentTiming.SetCustomInputIndex(1);

            Input_AdjustmentFrequency.Name = "Adjustment Frequency (in Seconds)";
            Input_AdjustmentFrequency.SetInt(5);

            Input_TrailingOffset.Name = "Trailing Offset (in Ticks)";
            Input_TrailingOffset.SetInt(2);

            Input_ACSButtonNumber.Name = "ACS Control Bar Button # for Enable/Disable";
            Input_ACSButtonNumber.SetInt(1);
            Input_ACSButtonNumber.SetIntLimits(1, MAX_ACS_CONTROL_BAR_BUTTONS);

            Input_PositionType.  Name = "Position Type";
            Input_PositionType.SetCustomInputStrings("All Positions;Long Only;Short Only");
            Input_PositionType.SetCustomInputIndex(0);

            Input_DetailedLogging.Name = "Detailed Logging (for debugging)";
            Input_DetailedLogging.SetYesNo(0);

            Input_OnlyModifyInOneDirection.Name = "Only Modify Stops In One Direction";
            Input_OnlyModifyInOneDirection.SetYesNo(false);

            Input_Version.SetInt(2);

            Input_AllowZeroValues.Name = "Allow Zero Values";
            Input_AllowZeroValues.SetYesNo(false);

            Input_AllowNegativeValues.Name = "Allow Negative Values";
            Input_AllowNegativeValues.SetYesNo(false);

            Input_AllowCustomPropertiesForControlBarButton.Name = "Allow Custom 'Properties' for Control Bar Button";
            Input_AllowCustomPropertiesForControlBarButton.SetYesNo(false);

            Input_AttachedOrderToManage.Name = "Attached Order to Manage";
            Input_AttachedOrderToManage.SetCustomInputStrings("Nearest;OCO Group 1;OCO Group 2;OCO Group 3;OCO Group 4;OCO Group 5;OCO Group 6;OCO Group 7;OCO Group 8");
            Input_AttachedOrderToManage.SetCustomInputIndex(0);

            Input_ChangeToOffAllOtherCustomStudyButtonsOnEnable.Name = "Change To Off All Other Custom Study Buttons On Enable";
            Input_ChangeToOffAllOtherCustomStudyButtonsOnEnable.SetYesNo(false);

            // Do not receive mouse pointer events when ACS Tool Control Bar button is pressed in/enabled
            sc.ReceivePointerEvents = false;

            //sc.CancelAllOrdersOnEntries = false;

            return;
        }

        //References to persistent variables
        SCDateTime& LastAdjustmentDateTime = sc.GetPersistentSCDateTime(1);

        int& MenuID = sc.GetPersistentInt(1);

        int & LastLogMessageIdentifier =sc.GetPersistentInt(2);

        // Handle pointer events

        if (sc.IsFullRecalculation)//Is full recalculation
        {
            // set ACS Tool Control Bar tool tip
            sc.SetCustomStudyControlBarButtonHoverText(Input_ACSButtonNumber.GetInt(), "Enable/Disable Trade Management by Study");

            // set Custom Study Control Bar button text if the input to allow custom properties is not true. Otherwise, rely upon what the user sets.
            if (!Input_AllowCustomPropertiesForControlBarButton.GetYesNo())
            {
                sc.SetCustomStudyControlBarButtonShortCaption(Input_ACSButtonNumber.GetInt(), "TradeMan\nStudy");
            }

            sc.SetCustomStudyControlBarButtonEnable(Input_ACSButtonNumber.GetInt(), Input_Enabled.GetBoolean() );

            LastLogMessageIdentifier = 0;
        }

        if (Input_ChangeToOffAllOtherCustomStudyButtonsOnEnable.GetYesNo())
        {
            if (sc.MenuEventID >= ACS_BUTTON_1 && sc.MenuEventID <= ACS_BUTTON_150)
            {
                sc.SetCustomStudyControlBarButtonEnable(sc.PriorSelectedCustomStudyControlBarButtonNumber, false);
            }
        }

        // Wait for an event to enable study if not already enabled
        if (sc.MenuEventID == Input_ACSButtonNumber.GetInt())
        {
            const int ButtonState = (sc.PointerEventType == SC_ACS_BUTTON_ON) ? 1 : 0;
            Input_Enabled.SetYesNo(ButtonState);
        }

        // Execute the order management

        // If study is not enabled, exit without doing anything
        if (Input_Enabled.GetYesNo() == 0)
        {
            if (Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 1)
            {
                LastLogMessageIdentifier = 1;
                sc.AddMessageToLog("Study not enabled.", 0);
            }

            return;
        }

        //For safety we must never do any order management while historical data is being downloaded.
        if (sc.ChartIsDownloadingHistoricalData(sc.ChartNumber))
        {
            if (Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 2)
            {
                LastLogMessageIdentifier = 2;
                sc.AddMessageToLog("No order adjustments performed because historical data is being downloaded for chart.", 0);
            }
            return;
        }

        sc.SendOrdersToTradeService = !sc.GlobalTradeSimulationIsOn;

        // Flat state is a good time to reset LastAdjustmentDateTime
        s_SCPositionData PositionData;
        sc.GetTradePosition (PositionData);

        if (PositionData.PositionQuantity == 0)
        {
            LastAdjustmentDateTime.Clear();

            if (Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 3)
            {
                LastLogMessageIdentifier = 3;
                sc.AddMessageToLog("Position does not exist.", 0);
            }

            return;
        }

        if (Input_PositionType.GetIndex() == 1 && PositionData.PositionQuantity < 0)
        {
            if (Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 4)
            {
                LastLogMessageIdentifier = 4;
                SCString  LogText("Position Type input is set to 'Long Only' and Position is Short. Position: ");
                SCString QuantityString;
                sc.OrderQuantityToString(PositionData.PositionQuantity, QuantityString);
                LogText += QuantityString;
                sc.AddMessageToLog(LogText, 0);
            }

            return;
        }

        if (Input_PositionType.GetIndex() == 2 && PositionData.PositionQuantity > 0)
        {
            if (Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 5)
            {
                LastLogMessageIdentifier = 5;
                SCString  LogText("Position Type input is set to 'Short Only' and Position is Long. Position: ");
                SCString QuantityString;
                sc.OrderQuantityToString(PositionData.PositionQuantity, QuantityString);
                LogText += QuantityString;
                sc.AddMessageToLog(LogText, 0);
            }

            return;
        }

        // If the timing is every N seconds, check that we have surpassed the set frequency
        if (Input_AdjustmentTiming.GetInt() == 0 && (sc.LatestDateTimeForLastBar - LastAdjustmentDateTime) < SCDateTime::SECONDS_Fraction(Input_AdjustmentFrequency.GetFloat()))
        {
            if (Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 6)
            {
                LastLogMessageIdentifier = 6;
                sc.AddMessageToLog("Time interval has not elapsed yet for 'Every N Seconds'.", 0);
            }
            return;
        }

        //  Update the LastAdjustmentDateTime

        LastAdjustmentDateTime = sc.LatestDateTimeForLastBar;

        // Retrieve the controlling subgraph array
        SCFloatArray ControllingSubgraph;
        sc.GetStudyArrayUsingID(Input_ControlSubgraphRef.GetStudyID(), Input_ControlSubgraphRef.GetSubgraphIndex(), ControllingSubgraph);

        // Based on Input settings, retrieve nearest stop/target order
        s_SCTradeOrder ExistingTargetOrStopOrder;
        int Result = 0;

        if (Input_OrderType.IntValue == 0)
        {
            if (Input_AttachedOrderToManage.GetIndex() == 0)
            {
                Result = sc.GetNearestStopOrder(ExistingTargetOrStopOrder);
            }
            else
            {
                Result = sc.GetStopOrderInOCOGroupNumber(Input_AttachedOrderToManage.GetIndex(), ExistingTargetOrStopOrder);
            }
        }
        else
        {
            if (Input_AttachedOrderToManage.GetIndex() == 0)
            {
                Result = sc.GetNearestTargetOrder(ExistingTargetOrStopOrder);
            }
            else
            {
                Result = sc.GetTargetOrderInOCOGroupNumber(Input_AttachedOrderToManage.GetIndex(), ExistingTargetOrStopOrder);
            }
        }

        if (Result == SCTRADING_ORDER_ERROR)
        {
            if (Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 7)
            {
                LastLogMessageIdentifier = 7;
                sc.AddMessageToLog("There is no Stop/Target Attached Order found.", 0);
            }
            return;
        }

        // only process open orders
        if (ExistingTargetOrStopOrder.OrderStatusCode != SCT_OSC_OPEN
            && ExistingTargetOrStopOrder.OrderStatusCode != SCT_OSC_HELD)
        {
            if (Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 8)
            {
                LastLogMessageIdentifier = 8;
                sc.AddMessageToLog("The found Stop/Target order is not in an Open/Held state.", 0);
            }
            return;
        }

        // Modify the order
        int BarIndex = sc.ArraySize - 1;

        const bool AdjustmentTimingIsOnBarClose = Input_AdjustmentTiming.GetIndex() == 1;
        if(AdjustmentTimingIsOnBarClose)//On Bar Close
        {
            BarIndex = sc.ArraySize - 2;
        }

        if (!Input_AllowZeroValues.GetYesNo() && ControllingSubgraph[BarIndex] == 0.0)
        {
            if (Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 9)
            {
                LastLogMessageIdentifier = 9;
                if (AdjustmentTimingIsOnBarClose)
                {
                    sc.AddMessageToLog("The 'Controlling Subgraph Reference' value is 0 at the bar prior to the last bar.", 0);
                }
                else
                {
                    sc.AddMessageToLog("The 'Controlling Subgraph Reference' value is 0 at the last bar.", 0);
                }

            }
            return;
        }

        if (!Input_AllowNegativeValues.GetYesNo() && ControllingSubgraph[BarIndex] < 0)
        {
            if (Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 15)
            {
                LastLogMessageIdentifier = 15;
                sc.AddMessageToLog("The 'Controlling Subgraph Reference' value is < 0 at the last bar.", 0);
            }
            return;
        }

        double NewPrice = 0.0;

        if (ExistingTargetOrStopOrder.BuySell == BSE_SELL)
        {
            NewPrice = ControllingSubgraph[BarIndex] - Input_TrailingOffset.GetInt() * sc.TickSize;
        }
        else if (ExistingTargetOrStopOrder.BuySell == BSE_BUY)
        {
            NewPrice = ControllingSubgraph[BarIndex] + Input_TrailingOffset.GetInt() * sc.TickSize;
        }
        else
            return;//Should never happen so no need for error logging.

        //Round the price to an actual order price.
        NewPrice= sc.RoundToTickSize(NewPrice);

        //If the price has not changed, then do not modify.
        if (sc.FormattedEvaluateUsingDoubles(ExistingTargetOrStopOrder.Price1, sc.BaseGraphValueFormat, EQUAL_OPERATOR, NewPrice, sc.BaseGraphValueFormat )
            || sc.FormattedEvaluateUsingDoubles(ExistingTargetOrStopOrder.LastModifyPrice1, sc.BaseGraphValueFormat, EQUAL_OPERATOR, NewPrice, sc.BaseGraphValueFormat)
            )
        {
            if (Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 10)
            {
                LastLogMessageIdentifier = 10;

                SCString LogMessage;
                LogMessage.Format("There is not a new price to modify the order to. Existing order: Price1=%f, LastModifyPrice1 =%f. New study subgraph price with offset=%f.", ExistingTargetOrStopOrder.Price1, ExistingTargetOrStopOrder.LastModifyPrice1, NewPrice);

                sc.AddMessageToLog(LogMessage, 0);
            }
            return;
        }

        // If modify stop price is in wrong direction, then do not modify.
        if (Input_OrderType.IntValue == 0 //Stops
            &&  Input_OnlyModifyInOneDirection.GetYesNo() &&
            ((PositionData.PositionQuantity < 0 && sc.FormattedEvaluateUsingDoubles(NewPrice, sc.BaseGraphValueFormat, GREATER_OPERATOR, ExistingTargetOrStopOrder.Price1, sc.BaseGraphValueFormat)) ||
            (PositionData.PositionQuantity > 0 && sc.FormattedEvaluateUsingDoubles(NewPrice, sc.BaseGraphValueFormat, LESS_OPERATOR, ExistingTargetOrStopOrder.Price1, sc.BaseGraphValueFormat)) )  )
        {
            if (Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 14)
            {
                LastLogMessageIdentifier = 14;
                sc.AddMessageToLog("Modify stop price is in wrong direction.", 0);
            }
            return;
        }

        s_SCNewOrder ModifyOrder;
        ModifyOrder.InternalOrderID = ExistingTargetOrStopOrder.InternalOrderID;
        ModifyOrder.Price1 = NewPrice;

        Result = sc.ModifyOrder(ModifyOrder);

        if (Result == 1 && Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 11)
        {
            LastLogMessageIdentifier = 11;
            SCString  LogText;
            LogText.Format("The Stop/Target order has been modified. Internal Order ID: %d. New Price: %f", ExistingTargetOrStopOrder.InternalOrderID, NewPrice);

            sc.AddMessageToLog( LogText , 0);
        }
        else if (Result == SCT_SKIPPED_AUTO_TRADING_DISABLED && Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 13)
        {
            LastLogMessageIdentifier = 13;
            SCString  LogText;
            LogText.Format("'Trade >> Auto Trading Enabled' is disabled.");

            sc.AddMessageToLog( LogText , 0);
        }
        else if (Result != 1 && Input_DetailedLogging.GetYesNo() && LastLogMessageIdentifier != 12)
        {
            LastLogMessageIdentifier = 12;
            SCString  LogText;
            LogText.Format("There was an error modifying the Stop/Target order. Internal Order ID: %d. New Price: %f. Error Code %d.", ExistingTargetOrStopOrder.InternalOrderID, NewPrice, Result);

            sc.AddMessageToLog( LogText , 0);
        }

    }