#include "sierrachart.h" typedef std::vector t_FloatVector; void TradingSpreadOrderEntry_DrawStatusText(SCStudyInterfaceRef sc); void TradingSpreadOrderEntry_UpdateTargetStopSubgraphs(SCStudyInterfaceRef sc, int BeginIndex, int EndIndex); /*==========================================================================*/ SCSFExport scsf_TradingSpreadOrderEntry(SCStudyInterfaceRef sc) { enum StudyStateEnum : int32_t { STATE_UNSET = 0 , STATE_DISABLED = 1 , STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER = 2 , STATE_MONITORING_FOR_TARGET_AND_STOP = 3 , STATE_DISABLED_AFTER_ORDERS_SENT = 4 , STATE_DISABLED_INVALID_ENTRY_PRICE = 5 }; enum SpreadComparisonPriceOperatorEnum : int32_t { LESS_EQUAL = 0 , GREATER_EQUAL = 1 }; SCSubgraphRef Subgraph_SpreadOrderPrice = sc.Subgraph[0]; SCSubgraphRef Subgraph_OnChartTextDisplay = sc.Subgraph[1]; SCSubgraphRef Subgraph_EntryPriceLine = sc.Subgraph[2]; SCSubgraphRef Subgraph_TargetPriceLine = sc.Subgraph[3]; SCSubgraphRef Subgraph_StopPriceLine = sc.Subgraph[4]; SCInputRef Input_IsActiveAndWaitingForSpreadTriggerPrice = sc.Input[0]; SCInputRef Input_Symbol1Symbol = sc.Input[1]; SCInputRef Input_Symbol1Side = sc.Input[2]; SCInputRef Input_Symbol1Quantity = sc.Input[3]; SCInputRef Input_Symbol2Symbol = sc.Input[4]; SCInputRef Input_Symbol2Side = sc.Input[5]; SCInputRef Input_Symbol2Quantity = sc.Input[6]; SCInputRef Input_Symbol3Symbol = sc.Input[7]; SCInputRef Input_Symbol3Side = sc.Input[8]; SCInputRef Input_Symbol3Quantity = sc.Input[9]; SCInputRef Input_Symbol4Symbol = sc.Input[10]; SCInputRef Input_Symbol4Side = sc.Input[11]; SCInputRef Input_Symbol4Quantity = sc.Input[12]; SCInputRef Input_SpreadEntryPrice = sc.Input[13]; SCInputRef Input_SpreadEntryPriceOperator = sc.Input[14]; SCInputRef Input_NumberOfBarsForSubgraphs = sc.Input[15]; SCInputRef Input_DrawZeroValues = sc.Input[16]; SCInputRef Input_UseTargetAfterFillOfSpreadEntry = sc.Input[17]; SCInputRef Input_TargetSpreadPrice = sc.Input[18]; SCInputRef Input_UseStopAfterFillOfSpreadEntry = sc.Input[19]; SCInputRef Input_StopSpreadPrice = sc.Input[20]; SCInputRef Input_DisableIfImmediateFillOfSpreadAfterSpreadPriceSet = sc.Input[21]; SCInputRef Input_UseFullContractValuesForSpreadPrices = sc.Input[22]; if (sc.SetDefaults) { sc.GraphName = "Trading: Spread Order Entry"; sc.AutoLoop = 0;//Manual looping sc.GraphRegion = 1; sc.ScaleRangeType= SCALE_AUTO; sc.ValueFormat = VALUEFORMAT_INHERITED; sc.UpdateAlways = 1; sc.ReceivePointerEvents = ACS_RECEIVE_POINTER_EVENTS_ALWAYS; Subgraph_SpreadOrderPrice.Name = "Spread Price"; Subgraph_SpreadOrderPrice.DrawStyle = DRAWSTYLE_LINE; Subgraph_SpreadOrderPrice.PrimaryColor = RGB(0,128,255); Subgraph_SpreadOrderPrice.LineLabel = LL_DISPLAY_VALUE | LL_VALUE_ALIGN_VALUES_SCALE| LL_VALUE_ALIGN_CENTER | LL_DISPLAY_NAME | LL_NAME_ALIGN_LEFT_EDGE | LL_NAME_ALIGN_CENTER; Subgraph_SpreadOrderPrice.DrawZeros = true; Subgraph_OnChartTextDisplay.Name = "Chart Text Display"; Subgraph_OnChartTextDisplay.DrawStyle = DRAWSTYLE_CUSTOM_TEXT; Subgraph_OnChartTextDisplay.PrimaryColor = RGB(255, 165, 0); Subgraph_OnChartTextDisplay.LineWidth = 10; Subgraph_EntryPriceLine.Name = "Entry Price"; Subgraph_EntryPriceLine.DrawStyle = DRAWSTYLE_LINE; Subgraph_EntryPriceLine.LineWidth = 2; Subgraph_EntryPriceLine.PrimaryColor = RGB(0, 255, 128); Subgraph_EntryPriceLine.LineLabel = LL_DISPLAY_VALUE | LL_VALUE_ALIGN_VALUES_SCALE | LL_VALUE_ALIGN_CENTER | LL_DISPLAY_NAME | LL_NAME_ALIGN_LEFT_EDGE | LL_NAME_ALIGN_CENTER; Subgraph_EntryPriceLine.DrawZeros = true; Subgraph_TargetPriceLine.Name = "Target Price"; Subgraph_TargetPriceLine.DrawStyle = DRAWSTYLE_LINE; Subgraph_TargetPriceLine.LineWidth = 2; Subgraph_TargetPriceLine.PrimaryColor = RGB(255, 100, 0); Subgraph_TargetPriceLine.LineLabel = LL_DISPLAY_VALUE | LL_VALUE_ALIGN_VALUES_SCALE | LL_VALUE_ALIGN_CENTER | LL_DISPLAY_NAME | LL_NAME_ALIGN_LEFT_EDGE | LL_NAME_ALIGN_CENTER; Subgraph_TargetPriceLine.DrawZeros = true; Subgraph_StopPriceLine.Name = "Stop Price"; Subgraph_StopPriceLine.DrawStyle = DRAWSTYLE_LINE; Subgraph_StopPriceLine.LineWidth = 2; Subgraph_StopPriceLine.PrimaryColor = RGB(255, 100, 0); Subgraph_StopPriceLine.LineLabel = LL_DISPLAY_VALUE | LL_VALUE_ALIGN_VALUES_SCALE | LL_VALUE_ALIGN_CENTER | LL_DISPLAY_NAME | LL_NAME_ALIGN_LEFT_EDGE | LL_NAME_ALIGN_CENTER; Subgraph_StopPriceLine.DrawZeros = true; Input_IsActiveAndWaitingForSpreadTriggerPrice.Name = "Is Active and Waiting for Spread Trigger Price"; Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false); Input_Symbol1Symbol.Name = "Leg 1 Symbol"; Input_Symbol1Symbol.SetSymbol(""); Input_Symbol1Side.Name = "Leg 1 Side"; Input_Symbol1Side.SetCustomInputStrings("Buy;Sell"); Input_Symbol1Side.SetCustomInputIndex(0); Input_Symbol1Quantity.Name = "Leg 1 Quantity"; Input_Symbol1Quantity.SetInt(1); Input_Symbol2Symbol.Name = "Leg 2 Symbol"; Input_Symbol2Symbol.SetSymbol(""); Input_Symbol2Side.Name = "Leg 2 Side"; Input_Symbol2Side.SetCustomInputStrings("Buy;Sell"); Input_Symbol2Side.SetCustomInputIndex(1); Input_Symbol2Quantity.Name = "Leg 2 Quantity"; Input_Symbol2Quantity.SetInt(1); Input_Symbol3Symbol.Name = "Leg 3 Symbol"; Input_Symbol3Symbol.SetSymbol(""); Input_Symbol3Side.Name = "Leg 3 Side"; Input_Symbol3Side.SetCustomInputStrings("Buy;Sell"); Input_Symbol3Side.SetCustomInputIndex(0); Input_Symbol3Quantity.Name = "Leg 3 Quantity"; Input_Symbol3Quantity.SetInt(0); Input_Symbol4Symbol.Name = "Leg 4 Symbol"; Input_Symbol4Symbol.SetSymbol(""); Input_Symbol4Side.Name = "Leg 4 Side"; Input_Symbol4Side.SetCustomInputStrings("Buy;Sell"); Input_Symbol4Side.SetCustomInputIndex(0); Input_Symbol4Quantity.Name = "Leg 4 Quantity"; Input_Symbol4Quantity.SetInt(0); Input_SpreadEntryPrice.Name = "Spread Entry Price"; Input_SpreadEntryPrice.SetFloat(0); Input_SpreadEntryPriceOperator.Name = "Spread Entry Price Operator"; Input_SpreadEntryPriceOperator.SetCustomInputStrings("Less or Equal;Greater or Equal"); Input_SpreadEntryPriceOperator.SetCustomInputIndex(LESS_EQUAL); Input_NumberOfBarsForSubgraphs.Name = "Number of Bars for Subgraphs"; Input_NumberOfBarsForSubgraphs.SetInt(200); Input_NumberOfBarsForSubgraphs.SetIntLimits(1, 2000); Input_DrawZeroValues.Name = "Draw Zero Values"; Input_DrawZeroValues.SetYesNo(false); Input_UseTargetAfterFillOfSpreadEntry.Name = "Use Target after Fill of Spread Entry"; Input_UseTargetAfterFillOfSpreadEntry.SetYesNo(false); Input_TargetSpreadPrice.Name = "Target Spread Price"; Input_TargetSpreadPrice.SetFloat(0); Input_UseStopAfterFillOfSpreadEntry.Name = "Use Stop after Fill of Spread Entry"; Input_UseStopAfterFillOfSpreadEntry.SetYesNo(false); Input_StopSpreadPrice.Name = "Stop Spread Price"; Input_StopSpreadPrice.SetFloat(0); Input_DisableIfImmediateFillOfSpreadAfterSpreadPriceSet.Name = "Disable if Immediate Fill of Spread After Spread Price Set"; Input_DisableIfImmediateFillOfSpreadAfterSpreadPriceSet.SetYesNo(false); Input_UseFullContractValuesForSpreadPrices.Name = "Use Full Contract Values for Spread Prices"; Input_UseFullContractValuesForSpreadPrices.SetYesNo(false); sc.AllowMultipleEntriesInSameDirection = 1; sc.AllowOppositeEntryWithOpposingPositionOrOrders = 1; sc.CancelAllOrdersOnEntriesAndReversals = 0; sc.AllowOnlyOneTradePerBar = 0; sc.SupportReversals = 0; sc.AllowEntryWithWorkingOrders = 1; sc.SupportAttachedOrdersForTrading = 0; sc.UseGUIAttachedOrderSetting = 0; sc.MaximumPositionAllowed = 100000000; return; } sc.DisplayAsMainPriceGraph = false; int& r_EnableSpreadOrderEntryMenuID = sc.GetPersistentInt(1); int& r_DisableSpreadOrderEntryMenuID = sc.GetPersistentInt(2); int& r_DisabledActiveStatus = sc.GetPersistentInt(3); int& r_CurrentState = sc.GetPersistentInt(4); int &r_DrawingNumber = sc.GetPersistentInt(5); int& r_OrderModifyMode = sc.GetPersistentInt(6); //will both be 0 upon opening the Chartbook or first-time study added double& r_PriorSpreadEntryPrice = sc.GetPersistentDouble(1); int& r_SpreadEntryPriceHasChanged = sc.GetPersistentInt(7); SCString& r_CurrentOnScreenMessage = sc.GetPersistentSCString(1); const int PersistentHistoricalSpreadDataKey = 1; t_FloatVector* p_PersistentHistoricalSpreadData = reinterpret_cast(sc.GetPersistentPointer(PersistentHistoricalSpreadDataKey)); if (sc.LastCallToFunction) { // Be sure to remove the menu commands when study is removed sc.RemoveACSChartShortcutMenuItem(sc.ChartNumber, r_EnableSpreadOrderEntryMenuID); sc.RemoveACSChartShortcutMenuItem(sc.ChartNumber, r_DisableSpreadOrderEntryMenuID); if (p_PersistentHistoricalSpreadData != NULL) { delete p_PersistentHistoricalSpreadData; sc.SetPersistentPointer(PersistentHistoricalSpreadDataKey, NULL); } return; } if (p_PersistentHistoricalSpreadData == NULL) { p_PersistentHistoricalSpreadData = new t_FloatVector; if (p_PersistentHistoricalSpreadData != NULL) sc.SetPersistentPointer(PersistentHistoricalSpreadDataKey, p_PersistentHistoricalSpreadData); } if (p_PersistentHistoricalSpreadData == NULL) return; t_FloatVector& r_PersistentHistoricalSpreadData = *p_PersistentHistoricalSpreadData; r_PersistentHistoricalSpreadData.resize(Input_NumberOfBarsForSubgraphs.GetInt()); // this will be true when study is added to chart, and when Chartbook is opened with study on chart. This is done as a safety. if (r_DisabledActiveStatus == 0) { Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false); r_CurrentState = STATE_DISABLED; r_DisabledActiveStatus = 1; } sc.SendOrdersToTradeService = sc.GlobalTradeSimulationIsOn ? 0 : 1; //sc.SendOrdersToTradeService = 0;// TEMPORARY: Safety during development and testing if (sc.IsFullRecalculation) { r_DrawingNumber = 0; if (Input_NumberOfBarsForSubgraphs.GetInt() == 0) Input_NumberOfBarsForSubgraphs.SetInt(100); Subgraph_SpreadOrderPrice.DrawZeros = Input_DrawZeroValues.GetBoolean(); Subgraph_EntryPriceLine.DrawZeros = Input_DrawZeroValues.GetBoolean(); if (Input_UseTargetAfterFillOfSpreadEntry.GetYesNo()) Subgraph_TargetPriceLine.DrawZeros = Input_DrawZeroValues.GetBoolean(); else Subgraph_TargetPriceLine.DrawZeros = false; if (Input_UseStopAfterFillOfSpreadEntry.GetYesNo()) Subgraph_StopPriceLine.DrawZeros = Input_DrawZeroValues.GetBoolean(); else Subgraph_StopPriceLine.DrawZeros = false; if (r_EnableSpreadOrderEntryMenuID <= 0) { r_EnableSpreadOrderEntryMenuID = sc.AddACSChartShortcutMenuItem(sc.ChartNumber, "Enable Spread Order Entry"); } if (r_DisableSpreadOrderEntryMenuID <= 0) { r_DisableSpreadOrderEntryMenuID = sc.AddACSChartShortcutMenuItem(sc.ChartNumber, "Disable Spread Order Entry"); } if (r_CurrentState == STATE_UNSET) r_CurrentState = STATE_DISABLED; } if (Input_IsActiveAndWaitingForSpreadTriggerPrice.GetYesNo()) { sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_EnableSpreadOrderEntryMenuID, 0); sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_DisableSpreadOrderEntryMenuID, 1); } else { sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_EnableSpreadOrderEntryMenuID, 1); sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_DisableSpreadOrderEntryMenuID, 0); } if (!sc.ChartTradeModeEnabled) { r_CurrentOnScreenMessage = "Chart Trade Mode is not Enabled"; TradingSpreadOrderEntry_DrawStatusText(sc); return; } // handle set trigger price menu clicks if (sc.MenuEventID != 0 && sc.MenuEventID == r_EnableSpreadOrderEntryMenuID) { Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(true); if (r_CurrentState == STATE_DISABLED || r_CurrentState == STATE_DISABLED_AFTER_ORDERS_SENT || r_CurrentState == STATE_DISABLED_INVALID_ENTRY_PRICE) { r_CurrentState = STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER; r_CurrentOnScreenMessage = "Monitoring for spread entry price"; TradingSpreadOrderEntry_DrawStatusText(sc); } sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_EnableSpreadOrderEntryMenuID, 0); sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_DisableSpreadOrderEntryMenuID, 1); } else if (sc.MenuEventID != 0 && sc.MenuEventID == r_DisableSpreadOrderEntryMenuID) { Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false); r_CurrentState = STATE_DISABLED; r_CurrentOnScreenMessage = "Disabled"; TradingSpreadOrderEntry_DrawStatusText(sc); sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_EnableSpreadOrderEntryMenuID, 1); sc.SetACSChartShortcutMenuItemEnabled(sc.ChartNumber, r_DisableSpreadOrderEntryMenuID, 0); } const int NUM_SPREAD_ORDERS = 4; SCString SpreadSymbols[NUM_SPREAD_ORDERS]; int32_t SpreadSides[NUM_SPREAD_ORDERS]; uint32_t SpreadQuantities[NUM_SPREAD_ORDERS]; SpreadSymbols[0] = Input_Symbol1Symbol.GetSymbol(); SpreadSymbols[1] = Input_Symbol2Symbol.GetSymbol(); SpreadSymbols[2] = Input_Symbol3Symbol.GetSymbol(); SpreadSymbols[3] = Input_Symbol4Symbol.GetSymbol(); SpreadSides[0] = Input_Symbol1Side.GetIndex(); SpreadSides[1] = Input_Symbol2Side.GetIndex(); SpreadSides[2] = Input_Symbol3Side.GetIndex(); SpreadSides[3] = Input_Symbol4Side.GetIndex(); SpreadQuantities[0] = Input_Symbol1Quantity.GetInt(); SpreadQuantities[1] = Input_Symbol2Quantity.GetInt(); SpreadQuantities[2] = Input_Symbol3Quantity.GetInt(); SpreadQuantities[3] = Input_Symbol4Quantity.GetInt(); if (SpreadSymbols[0].IsEmpty() || SpreadSymbols[1].IsEmpty() || SpreadQuantities[0] == 0 || SpreadQuantities[1] == 0) { r_CurrentOnScreenMessage = "Settings not complete for spread entry"; TradingSpreadOrderEntry_DrawStatusText(sc); return; } const bool UseFullContractValues = Input_UseFullContractValuesForSpreadPrices.GetYesNo(); double CurrentSpreadValue = 0.0; bool IsSpreadPriceValid = true; int NumberOfPricesInCalculation = 0; int NumberOfLegs = 0; int NumberOfBuys = 0; int NumberOfSells = 0; for (int SpreadOrderDataIndex = 0; SpreadOrderDataIndex < NUM_SPREAD_ORDERS; ++SpreadOrderDataIndex ) { if (SpreadQuantities[SpreadOrderDataIndex] == 0.0) continue; if (SpreadSymbols[SpreadOrderDataIndex].IsEmpty()) continue; ++NumberOfLegs; s_SCBasicSymbolData BasicSymbolData; sc.GetBasicSymbolData(SpreadSymbols[SpreadOrderDataIndex].GetChars(), BasicSymbolData, true); if (BasicSymbolData.Bid == 0.0f && BasicSymbolData.Ask == 0.0f) { r_CurrentOnScreenMessage = "Bid and Ask prices are zero"; TradingSpreadOrderEntry_DrawStatusText(sc); IsSpreadPriceValid = false; } if (SpreadSides[SpreadOrderDataIndex] == 0)//Buy { const float AskPrice = BasicSymbolData.Ask * BasicSymbolData.DisplayPriceMultiplier; const double IndividualSymbolValue = AskPrice * SpreadQuantities[SpreadOrderDataIndex]; if (UseFullContractValues && BasicSymbolData.CurrencyValuePerTick > 0) { CurrentSpreadValue += (IndividualSymbolValue / BasicSymbolData.TickSize * BasicSymbolData.CurrencyValuePerTick); } else CurrentSpreadValue += IndividualSymbolValue; ++NumberOfPricesInCalculation; NumberOfBuys++; } else if (SpreadSides[SpreadOrderDataIndex] == 1)//Sell { const float BidPrice = BasicSymbolData.Bid * BasicSymbolData.DisplayPriceMultiplier; const double IndividualSymbolValue = BidPrice * SpreadQuantities[SpreadOrderDataIndex]; if (UseFullContractValues && BasicSymbolData.CurrencyValuePerTick > 0) { CurrentSpreadValue -= (IndividualSymbolValue / BasicSymbolData.TickSize * BasicSymbolData.CurrencyValuePerTick); } else CurrentSpreadValue -= IndividualSymbolValue; ++NumberOfPricesInCalculation; NumberOfSells++; } } double RegionHigh = 0.0; double RegionLow = 0.0; sc.GetGraphVisibleHighAndLow(RegionHigh, RegionLow); bool AllowModify = !((RegionHigh == 0.0 && RegionLow == 0.0) || sc.ActiveToolYValue > RegionHigh || sc.ActiveToolYValue < RegionLow); if (!AllowModify) r_OrderModifyMode = 0; if (AllowModify && Input_UseTargetAfterFillOfSpreadEntry.GetYesNo() && r_OrderModifyMode != 2 && r_OrderModifyMode != 3)//Not modifying Stop or Entry { double TargetPrice = Input_TargetSpreadPrice.GetDouble(); int YPosition = sc.RegionValueToYPixelCoordinate(static_cast(TargetPrice), sc.GraphRegion); if (sc.PointerEventType == SC_POINTER_BUTTON_DOWN) { if(YPosition >= sc.PointerVertWindowCoord - 5 && YPosition <= sc.PointerVertWindowCoord + 5) r_OrderModifyMode = 1; } if (sc.PointerEventType == SC_POINTER_BUTTON_UP) r_OrderModifyMode = 0; if (r_OrderModifyMode == 1 && sc.PointerEventType == SC_POINTER_MOVE) { Input_TargetSpreadPrice.SetFloat(static_cast(sc.RoundToTickSize(sc.ActiveToolYValue))); } } if (AllowModify && Input_UseStopAfterFillOfSpreadEntry.GetYesNo() && r_OrderModifyMode != 1 && r_OrderModifyMode != 3) { double StopPrice = Input_StopSpreadPrice.GetDouble(); int Yposition = sc.RegionValueToYPixelCoordinate(static_cast(StopPrice), sc.GraphRegion); if (sc.PointerEventType == SC_POINTER_BUTTON_DOWN) { if (Yposition >= sc.PointerVertWindowCoord - 5 && Yposition <= sc.PointerVertWindowCoord + 5) r_OrderModifyMode = 2; } if (sc.PointerEventType == SC_POINTER_BUTTON_UP) r_OrderModifyMode = 0; if (r_OrderModifyMode == 2 && sc.PointerEventType == SC_POINTER_MOVE) { Input_StopSpreadPrice.SetFloat(static_cast(sc.RoundToTickSize(sc.ActiveToolYValue))); } } if (AllowModify && r_OrderModifyMode != 1 && r_OrderModifyMode != 2) { double EntryPrice = Input_SpreadEntryPrice.GetDouble(); int YPosition = sc.RegionValueToYPixelCoordinate(static_cast(EntryPrice), sc.GraphRegion); if (sc.PointerEventType == SC_POINTER_BUTTON_DOWN) { if (YPosition >= sc.PointerVertWindowCoord - 5 && YPosition <= sc.PointerVertWindowCoord + 5) r_OrderModifyMode = 3; } if (sc.PointerEventType == SC_POINTER_BUTTON_UP) r_OrderModifyMode = 0; if (r_OrderModifyMode == 3 && sc.PointerEventType == SC_POINTER_MOVE) { Input_SpreadEntryPrice.SetFloat(static_cast(sc.RoundToTickSize(sc.ActiveToolYValue))); } } //Check here for changes with spread order entry price if (r_PriorSpreadEntryPrice != Input_SpreadEntryPrice.GetFloat()) r_SpreadEntryPriceHasChanged = 1; r_PriorSpreadEntryPrice = Input_SpreadEntryPrice.GetFloat(); int BarStartIndex = sc.UpdateStartIndex; int EarliestIndex = sc.ArraySize - Input_NumberOfBarsForSubgraphs.GetInt(); if (EarliestIndex > BarStartIndex) { BarStartIndex = EarliestIndex; } if (r_OrderModifyMode != 0 && BarStartIndex > EarliestIndex) BarStartIndex = EarliestIndex; // zero values before the bar starting index for the values for (int BarIndex = sc.UpdateStartIndex; BarIndex < BarStartIndex; BarIndex++) { Subgraph_EntryPriceLine[BarIndex] = 0; Subgraph_TargetPriceLine[BarIndex] = 0; Subgraph_StopPriceLine[BarIndex] = 0; Subgraph_SpreadOrderPrice[BarIndex] = 0; } for (int BarIndex = BarStartIndex; BarIndex < sc.ArraySize; BarIndex++) { Subgraph_EntryPriceLine[BarIndex] = Input_SpreadEntryPrice.GetFloat(); if (Input_UseTargetAfterFillOfSpreadEntry.GetYesNo()) Subgraph_TargetPriceLine[BarIndex] = Input_TargetSpreadPrice.GetFloat(); if (Input_UseStopAfterFillOfSpreadEntry.GetYesNo()) Subgraph_StopPriceLine[BarIndex] = Input_StopSpreadPrice.GetFloat(); } if (sc.IsFullRecalculation ) { const int VectorSize = static_cast(r_PersistentHistoricalSpreadData.size()); int OutputIndex = sc.ArraySize - VectorSize; for (int Index = 0; Index < VectorSize; ++Index) { Subgraph_SpreadOrderPrice[OutputIndex] = r_PersistentHistoricalSpreadData[Index]; ++OutputIndex; } } else if(IsSpreadPriceValid && Subgraph_SpreadOrderPrice[sc.ArraySize - 1] != static_cast(CurrentSpreadValue)) { //shift the current spread price values back for (int BarIndex = EarliestIndex; BarIndex < sc.ArraySize - 1; BarIndex++) { Subgraph_SpreadOrderPrice[BarIndex] = Subgraph_SpreadOrderPrice[BarIndex + 1]; } const int VectorSize = static_cast(r_PersistentHistoricalSpreadData.size()); for (int Index = 0; Index < VectorSize-1; ++Index) { r_PersistentHistoricalSpreadData[Index] = r_PersistentHistoricalSpreadData[Index + 1]; } } if (IsSpreadPriceValid) { Subgraph_SpreadOrderPrice[sc.ArraySize - 1] = static_cast(CurrentSpreadValue); if (!r_PersistentHistoricalSpreadData.empty()) r_PersistentHistoricalSpreadData.back() = static_cast(CurrentSpreadValue); } for (int BarIndex = EarliestIndex - 1; BarIndex >= 0; --BarIndex) { if (Subgraph_SpreadOrderPrice[BarIndex] == 0 && Subgraph_EntryPriceLine[BarIndex] == 0 && Subgraph_TargetPriceLine[BarIndex] == 0 && Subgraph_StopPriceLine[BarIndex] == 0) break; Subgraph_SpreadOrderPrice[BarIndex] = 0; Subgraph_EntryPriceLine[BarIndex] = 0; Subgraph_TargetPriceLine[BarIndex] = 0; Subgraph_StopPriceLine[BarIndex] = 0; } if (sc.IsFullRecalculation)//Do not do any spread price evaluation on a full recalculation { if (r_CurrentState == STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER || r_CurrentState == STATE_MONITORING_FOR_TARGET_AND_STOP) { r_CurrentOnScreenMessage = "Waiting to begin monitoring"; TradingSpreadOrderEntry_DrawStatusText(sc); } return; } if (NumberOfPricesInCalculation != NumberOfLegs) { r_CurrentOnScreenMessage = "Number of prices in calculation != to the number of legs"; TradingSpreadOrderEntry_DrawStatusText(sc); return; } if (NumberOfLegs == 2 && NumberOfBuys != NumberOfSells) { r_CurrentOnScreenMessage = "Disabled: Number of buys != number of sells for 2 legs"; Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false); r_CurrentState = STATE_DISABLED; TradingSpreadOrderEntry_DrawStatusText(sc); return; } bool UseTargetOrStop = Input_UseTargetAfterFillOfSpreadEntry.GetYesNo() || Input_UseStopAfterFillOfSpreadEntry.GetYesNo(); if (!Input_IsActiveAndWaitingForSpreadTriggerPrice.GetYesNo() || (r_CurrentState == STATE_MONITORING_FOR_TARGET_AND_STOP && !UseTargetOrStop)) { if (r_CurrentState != STATE_DISABLED && r_CurrentState != STATE_DISABLED_AFTER_ORDERS_SENT && r_CurrentState != STATE_DISABLED_INVALID_ENTRY_PRICE) r_CurrentState = STATE_DISABLED; if(r_CurrentState != STATE_DISABLED_AFTER_ORDERS_SENT && r_CurrentState != STATE_DISABLED_INVALID_ENTRY_PRICE) r_CurrentOnScreenMessage = "Disabled"; TradingSpreadOrderEntry_DrawStatusText(sc); return; } if(r_CurrentState == STATE_DISABLED || r_CurrentState == STATE_DISABLED_AFTER_ORDERS_SENT || r_CurrentState == STATE_DISABLED_INVALID_ENTRY_PRICE) { r_CurrentState = STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER; } if (r_CurrentState == STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER) { r_CurrentOnScreenMessage = "Monitoring for spread entry price"; TradingSpreadOrderEntry_DrawStatusText(sc); } if (r_OrderModifyMode != 0) return; if (!IsSpreadPriceValid) return; int NumberOfPricesToCompare = 0; double SpreadPricesToCompareTo[2] = {}; SpreadComparisonPriceOperatorEnum ComparisonOperators[2] = {LESS_EQUAL}; if (r_CurrentState == STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER) { SpreadPricesToCompareTo[0]= Input_SpreadEntryPrice.GetFloat(); NumberOfPricesToCompare = 1; ComparisonOperators[0] = static_cast(Input_SpreadEntryPriceOperator.GetIndex()); } else if (r_CurrentState == STATE_MONITORING_FOR_TARGET_AND_STOP) { // In the case of greater or equal, invert the operator for the exit order that is lower than the entry. // In the case of less or equal, invert the operator for the exit order that is higher than the entry. if (Input_UseTargetAfterFillOfSpreadEntry.GetYesNo()) { SpreadPricesToCompareTo[NumberOfPricesToCompare] = Input_TargetSpreadPrice.GetFloat(); if (Input_SpreadEntryPriceOperator.GetIndex() == LESS_EQUAL && SpreadPricesToCompareTo[NumberOfPricesToCompare] > Input_SpreadEntryPrice.GetFloat()) { ComparisonOperators[NumberOfPricesToCompare] = GREATER_EQUAL; } else if (Input_SpreadEntryPriceOperator.GetIndex() == GREATER_EQUAL && SpreadPricesToCompareTo[NumberOfPricesToCompare] < Input_SpreadEntryPrice.GetFloat()) { ComparisonOperators[NumberOfPricesToCompare] = LESS_EQUAL; } else { ComparisonOperators[NumberOfPricesToCompare] = static_cast(Input_SpreadEntryPriceOperator.GetIndex()); } NumberOfPricesToCompare++; } if (Input_UseStopAfterFillOfSpreadEntry.GetYesNo()) { SpreadPricesToCompareTo[NumberOfPricesToCompare] = Input_StopSpreadPrice.GetFloat(); if (Input_SpreadEntryPriceOperator.GetIndex() == LESS_EQUAL && SpreadPricesToCompareTo[NumberOfPricesToCompare] > Input_SpreadEntryPrice.GetFloat()) { ComparisonOperators[NumberOfPricesToCompare] = GREATER_EQUAL; } else if (Input_SpreadEntryPriceOperator.GetIndex() == GREATER_EQUAL && SpreadPricesToCompareTo[NumberOfPricesToCompare] < Input_SpreadEntryPrice.GetFloat()) { ComparisonOperators[NumberOfPricesToCompare] = LESS_EQUAL; } else { ComparisonOperators[NumberOfPricesToCompare] = static_cast(Input_SpreadEntryPriceOperator.GetIndex()); } NumberOfPricesToCompare++; } } bool SpreadPriceMet = false; for(int Index = 0; Index < NumberOfPricesToCompare; Index++) { if (ComparisonOperators[Index] == LESS_EQUAL) { if (CurrentSpreadValue <= SpreadPricesToCompareTo[Index]) { if (r_CurrentState == STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER) r_CurrentOnScreenMessage = "Triggered - "; else r_CurrentOnScreenMessage = "Target/Stop Triggered - "; SpreadPriceMet = true; } } else if (ComparisonOperators[Index] == GREATER_EQUAL) { if (CurrentSpreadValue >= SpreadPricesToCompareTo[Index]) { if (r_CurrentState == STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER) r_CurrentOnScreenMessage = "Triggered - "; else r_CurrentOnScreenMessage = "Target/Stop Triggered - "; SpreadPriceMet = true; } } else return; } if (SpreadPriceMet) { if (r_SpreadEntryPriceHasChanged && Input_DisableIfImmediateFillOfSpreadAfterSpreadPriceSet.GetYesNo()) { r_SpreadEntryPriceHasChanged = 0; Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false); r_CurrentState = STATE_DISABLED_INVALID_ENTRY_PRICE; r_CurrentOnScreenMessage = "Disabled: DisableIfImmediateFillOfSpreadAfterSpreadPriceSet"; TradingSpreadOrderEntry_DrawStatusText(sc); return; } r_SpreadEntryPriceHasChanged = 0; } else { return; } if (!Input_IsActiveAndWaitingForSpreadTriggerPrice.GetYesNo()) return;//this will not be reached but is here for safety. if (r_CurrentState != STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER && r_CurrentState != STATE_MONITORING_FOR_TARGET_AND_STOP) return; //From this point on *must* get to end of function for safety so state will be set to disabled or monitoring for target or stop. r_CurrentOnScreenMessage += sc.FormatDateTime(sc.CurrentSystemDateTime); r_CurrentOnScreenMessage += ". "; bool Successful = false; for (int SpreadOrderDataIndex = 0; SpreadOrderDataIndex < NUM_SPREAD_ORDERS; ++SpreadOrderDataIndex) { if (SpreadQuantities[SpreadOrderDataIndex] == 0.0) continue; if (SpreadSymbols[SpreadOrderDataIndex].IsEmpty()) continue; if (SpreadOrderDataIndex > 0) r_CurrentOnScreenMessage += ", "; // At this point it is safe to submit the market order s_SCNewOrder NewOrder; NewOrder.OrderQuantity = SpreadQuantities[SpreadOrderDataIndex]; NewOrder.OrderType = SCT_ORDERTYPE_MARKET; NewOrder.TimeInForce = SCT_TIF_DAY; NewOrder.Symbol = SpreadSymbols[SpreadOrderDataIndex]; NewOrder.TradeAccount = sc.SelectedTradeAccount; NewOrder.TextTag = "Spread order entry leg"; int Result = 0; if (r_CurrentState == STATE_MONITORING_FOR_TARGET_AND_STOP && UseTargetOrStop) { if (SpreadSides[SpreadOrderDataIndex] == 0)//Buy Result = static_cast(sc.SellOrder(NewOrder)); else if (SpreadSides[SpreadOrderDataIndex] == 1)//Sell Result = static_cast(sc.BuyOrder(NewOrder)); } else { if (SpreadSides[SpreadOrderDataIndex] == 0)//Buy Result = static_cast(sc.BuyOrder(NewOrder)); else if (SpreadSides[SpreadOrderDataIndex] == 1)//Sell Result = static_cast(sc.SellOrder(NewOrder)); } if (Result < 0) { r_CurrentOnScreenMessage = "Submission error: "; r_CurrentOnScreenMessage += sc.GetTradingErrorTextMessage(Result); } else { Successful = true; r_CurrentOnScreenMessage += NewOrder.Symbol; r_CurrentOnScreenMessage += "*"; if (r_CurrentState == STATE_MONITORING_FOR_TARGET_AND_STOP && UseTargetOrStop) { if (SpreadSides[SpreadOrderDataIndex] == 0) r_CurrentOnScreenMessage += "-"; } else { if (SpreadSides[SpreadOrderDataIndex] == 1) r_CurrentOnScreenMessage += "-"; } r_CurrentOnScreenMessage.AppendFormat("%d", NewOrder.OrderQuantity); SCString MessageText("Market order submitted for spread leg "); MessageText += NewOrder.Symbol; sc.AddMessageToTradeServiceLog(MessageText, false); } } //Disable or monitor for target or stop if (!UseTargetOrStop) { Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false);//Disable r_CurrentOnScreenMessage += ". Not Monitoring"; r_CurrentState = STATE_DISABLED_AFTER_ORDERS_SENT; Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false);//Disable } else { if(r_CurrentState == STATE_MONITORING_FOR_SPREAD_PRICE_TRIGGER) { r_CurrentOnScreenMessage += ". Monitoring Target/Stop"; r_CurrentState = STATE_MONITORING_FOR_TARGET_AND_STOP; } else { r_CurrentOnScreenMessage += ". Not Monitoring"; r_CurrentState = STATE_DISABLED_AFTER_ORDERS_SENT; Input_IsActiveAndWaitingForSpreadTriggerPrice.SetYesNo(false);//Disable } } TradingSpreadOrderEntry_DrawStatusText(sc); } /*==========================================================================*/ void TradingSpreadOrderEntry_DrawStatusText(SCStudyInterfaceRef sc ) { SCSubgraphRef Subgraph_OnChartTextDisplay = sc.Subgraph[1]; int &r_DrawingNumber = sc.GetPersistentInt(5); SCString& r_CurrentOnScreenMessage = sc.GetPersistentSCString(1); s_UseTool Tool; Tool.Clear(); Tool.ChartNumber = sc.ChartNumber; Tool.DrawingType = DRAWING_TEXT; Tool.BeginDateTime = 10; Tool.Region = sc.GraphRegion; Tool.BeginValue = 90; Tool.UseRelativeVerticalValues = true; Tool.Color = Subgraph_OnChartTextDisplay.PrimaryColor; Tool.FontBold = true; Tool.Text = r_CurrentOnScreenMessage; Tool.FontSize = Subgraph_OnChartTextDisplay.LineWidth; Tool.AddMethod = UTAM_ADD_OR_ADJUST; Tool.ReverseTextColor = false; if (r_DrawingNumber != 0) Tool.LineNumber = r_DrawingNumber; if(sc.UseTool(Tool)) r_DrawingNumber = Tool.LineNumber; } /*==========================================================================*/ void TradingSpreadOrderEntry_UpdateTargetStopSubgraphs(SCStudyInterfaceRef sc, int BeginIndex, int EndIndex) { SCSubgraphRef Subgraph_TargetPriceLine = sc.Subgraph[3]; SCSubgraphRef Subgraph_StopPriceLine = sc.Subgraph[4]; SCInputRef Input_UseTargetAfterFillOfSpreadEntry = sc.Input[17]; SCInputRef Input_TargetSpreadPrice = sc.Input[18]; SCInputRef Input_UseStopAfterFillOfSpreadEntry = sc.Input[19]; SCInputRef Input_StopSpreadPrice = sc.Input[20]; for (int BarIndex = BeginIndex; BarIndex < EndIndex; BarIndex++) { if (Input_UseTargetAfterFillOfSpreadEntry.GetYesNo()) Subgraph_TargetPriceLine[BarIndex] = Input_TargetSpreadPrice.GetFloat(); if (Input_UseStopAfterFillOfSpreadEntry.GetYesNo()) Subgraph_StopPriceLine[BarIndex] = Input_StopSpreadPrice.GetFloat(); } }