#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.SetCustomStudyControlBarButtonText(Input_ACSButtonNumber.GetInt(), "TMS"); 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); } }