Support Board
Date/Time: Sat, 23 Nov 2024 12:51:25 +0000
[Programming Help] - Move stop to breakeven: auto trading system
View Count: 260
[2024-08-26 13:27:11] |
User357489 - Posts: 72 |
Hi SC Engineering/wider community, here's my auto trading system in its current iteration. As im watching it open and close trades, i notice that it isnt actually modifying the stop and moving it to breakeven as per my attempts here: #include "sierrachart.h" not overly sure where i may have gone wrong#include <unordered_map> #include <chrono> SCDLLName("Sweep Genie") struct TradeInfo { std::chrono::time_point<std::chrono::steady_clock> startTime; double startPrice; bool tradeEntered; int positionType; // 1 for buy, -1 for sell bool movedToBreakeven; // To track if stop has been moved to breakeven int stopOrderID; // To store the stop order ID }; std::unordered_map<int, TradeInfo> g_TradeInfo; SCSFExport scsf_SweepGenie(SCStudyInterfaceRef sc) { if (sc.SetDefaults) { sc.GraphName = "Sweep Genie"; sc.GraphRegion = 0; sc.AutoLoop = 0; // Manual looping // Grouping all Study ID inputs together sc.Input[10].Name = "VWAP Study ID"; sc.Input[10].SetInt(2); // VWAP Study ID sc.Input[11].Name = "Previous Day OHLC Study ID"; sc.Input[11].SetInt(3); // Previous Day OHLC Study ID sc.Input[12].Name = "Current Day OHLC Study ID"; sc.Input[12].SetInt(6); // Current Day OHLC Study ID // Other inputs sc.Input[1].Name = "Bid Highlight Color"; sc.Input[1].SetColor(RGB(0, 255, 0)); // Default bid highlight color sc.Input[2].Name = "Ask Highlight Color"; sc.Input[2].SetColor(RGB(255, 0, 0)); // Default ask highlight color sc.Input[3].Name = "Number of Levels to Check"; sc.Input[3].SetInt(5); // Number of bid/ask levels to check sc.Input[4].Name = "Order Quantity"; sc.Input[4].SetInt(1); // Default order quantity sc.Input[5].Name = "Enable Order Placement"; sc.Input[5].SetYesNo(0); // Default to not placing orders sc.Input[6].Name = "Stop Loss (Ticks)"; sc.Input[6].SetInt(10); // Stop loss in ticks from entry price sc.Input[7].Name = "Take Profit (Ticks)"; sc.Input[7].SetInt(20); // Take profit in ticks from entry price sc.Input[8].Name = "Trailing Stop Trigger (Ticks)"; sc.Input[8].SetInt(15); // Trailing Stop Trigger after specified ticks in profit sc.Input[9].Name = "Trailing Stop Offset (Ticks)"; sc.Input[9].SetInt(5); // Trailing Stop Offset from current price sc.Input[13].Name = "Recent Volume Threshold"; sc.Input[13].SetInt(100); // Default threshold value for recent volume sc.UsesMarketDepthData = 1; // Enable market depth data return; } int vwapStudyID = sc.Input[10].GetInt(); int prevOHLCStudyID = sc.Input[11].GetInt(); int currentOHLCStudyID = sc.Input[12].GetInt(); COLORREF bidHighlightColor = sc.Input[1].GetColor(); COLORREF askHighlightColor = sc.Input[2].GetColor(); int numLevelsToCheck = sc.Input[3].GetInt(); int orderQuantity = sc.Input[4].GetInt(); bool enableOrderPlacement = sc.Input[5].GetYesNo(); int stopLossTicks = sc.Input[6].GetInt(); int takeProfitTicks = sc.Input[7].GetInt(); int trailingStopTriggerTicks = sc.Input[8].GetInt(); int trailingStopOffsetTicks = sc.Input[9].GetInt(); int volumeThreshold = sc.Input[13].GetInt(); auto& tradeInfo = g_TradeInfo[sc.ChartNumber]; s_MarketDepthEntry depthEntry; const int maxOrders = 100; n_ACSIL::s_MarketOrderData bidOrders[maxOrders]; n_ACSIL::s_MarketOrderData askOrders[maxOrders]; sc.DeleteACSChartDrawing(sc.ChartNumber, TOOL_DELETE_ALL, 0); int bidMBOSum = 0; int askMBOSum = 0; int bidMarketDepthQuantitySum = 0; int askMarketDepthQuantitySum = 0; // Declare and initialize arrays for current and previous VWAP and OHLC levels SCFloatArray vwapArray, prevVwapArray, ohlcArray, prevOhlcArray; // Get arrays from the respective studies sc.GetStudyArrayUsingID(vwapStudyID, 1, vwapArray); // VWAP sc.GetStudyArrayUsingID(vwapStudyID, 10, prevVwapArray); // Previous VWAP sc.GetStudyArrayUsingID(currentOHLCStudyID, 1, ohlcArray); // Current OHLC sc.GetStudyArrayUsingID(prevOHLCStudyID, 1, prevOhlcArray); // Previous OHLC // Get the current prices for each level double currentVWAP = vwapArray[sc.ArraySize - 1]; double currentVWAP_0_5_Up = vwapArray[2]; double currentVWAP_0_5_Down = vwapArray[3]; double currentVWAP_1_0_Up = vwapArray[4]; double currentVWAP_1_0_Down = vwapArray[5]; double currentVWAP_1_5_Up = vwapArray[6]; double currentVWAP_1_5_Down = vwapArray[7]; double currentVWAP_2_0_Up = vwapArray[8]; double currentVWAP_2_0_Down = vwapArray[9]; double previousVWAP = prevVwapArray[sc.ArraySize - 1]; double previousVWAP_0_5_Up = prevVwapArray[11]; double previousVWAP_0_5_Down = prevVwapArray[12]; double previousVWAP_1_0_Up = prevVwapArray[13]; double previousVWAP_1_0_Down = prevVwapArray[14]; double previousVWAP_1_5_Up = prevVwapArray[15]; double previousVWAP_1_5_Down = prevVwapArray[16]; double previousVWAP_2_0_Up = prevVwapArray[17]; double previousVWAP_2_0_Down = prevVwapArray[18]; double currentOpen = ohlcArray[1]; double currentHigh = ohlcArray[2]; double currentLow = ohlcArray[3]; double currentHalfback = ohlcArray[9]; double previousOpen = prevOhlcArray[1]; double previousHigh = prevOhlcArray[2]; double previousLow = prevOhlcArray[3]; double previousClose = prevOhlcArray[4]; double previousHalfback = prevOhlcArray[9]; // Declare variables for scenarios bool scenarioForBuyMet = false; bool scenarioForSellMet = false; double Pref = (sc.Bid + sc.Ask) / 2; // Calculate Pref (mid-price or some representation of current price) SCString buyScenarioMessage; SCString sellScenarioMessage; // Buy scenarios within +/- 5 ticks of each level if ((Pref >= previousHigh - 5 * sc.TickSize && Pref <= previousHigh + 5 * sc.TickSize) || (Pref >= previousLow - 5 * sc.TickSize && Pref <= previousLow + 5 * sc.TickSize) || (Pref >= previousOpen - 5 * sc.TickSize && Pref <= previousOpen + 5 * sc.TickSize) || (Pref >= previousClose - 5 * sc.TickSize && Pref <= previousClose + 5 * sc.TickSize) || (Pref >= previousHalfback - 5 * sc.TickSize && Pref <= previousHalfback + 5 * sc.TickSize) || (Pref >= currentHigh - 5 * sc.TickSize && Pref <= currentHigh + 5 * sc.TickSize) || (Pref >= currentLow - 5 * sc.TickSize && Pref <= currentLow + 5 * sc.TickSize) || (Pref >= currentOpen - 5 * sc.TickSize && Pref <= currentOpen + 5 * sc.TickSize) || (Pref >= currentHalfback - 5 * sc.TickSize && Pref <= currentHalfback + 5 * sc.TickSize) || (Pref >= currentVWAP - 5 * sc.TickSize && Pref <= currentVWAP + 5 * sc.TickSize) || (Pref >= currentVWAP_0_5_Up - 5 * sc.TickSize && Pref <= currentVWAP_0_5_Up + 5 * sc.TickSize) || (Pref >= currentVWAP_0_5_Down - 5 * sc.TickSize && Pref <= currentVWAP_0_5_Down + 5 * sc.TickSize) || (Pref >= currentVWAP_1_0_Up - 5 * sc.TickSize && Pref <= currentVWAP_1_0_Up + 5 * sc.TickSize) || (Pref >= currentVWAP_1_0_Down - 5 * sc.TickSize && Pref <= currentVWAP_1_0_Down + 5 * sc.TickSize) || (Pref >= currentVWAP_1_5_Up - 5 * sc.TickSize && Pref <= currentVWAP_1_5_Up + 5 * sc.TickSize) || (Pref >= currentVWAP_1_5_Down - 5 * sc.TickSize && Pref <= currentVWAP_1_5_Down + 5 * sc.TickSize) || (Pref >= currentVWAP_2_0_Up - 5 * sc.TickSize && Pref <= currentVWAP_2_0_Up + 5 * sc.TickSize) || (Pref >= currentVWAP_2_0_Down - 5 * sc.TickSize && Pref <= currentVWAP_2_0_Down + 5 * sc.TickSize) || (Pref >= previousVWAP - 5 * sc.TickSize && Pref <= previousVWAP + 5 * sc.TickSize) || (Pref >= previousVWAP_0_5_Up - 5 * sc.TickSize && Pref <= previousVWAP_0_5_Up + 5 * sc.TickSize) || (Pref >= previousVWAP_0_5_Down - 5 * sc.TickSize && Pref <= previousVWAP_0_5_Down + 5 * sc.TickSize) || (Pref >= previousVWAP_1_0_Up - 5 * sc.TickSize && Pref <= previousVWAP_1_0_Up + 5 * sc.TickSize) || (Pref >= previousVWAP_1_0_Down - 5 * sc.TickSize && Pref <= previousVWAP_1_0_Down + 5 * sc.TickSize) || (Pref >= previousVWAP_1_5_Up - 5 * sc.TickSize && Pref <= previousVWAP_1_5_Up + 5 * sc.TickSize) || (Pref >= previousVWAP_1_5_Down - 5 * sc.TickSize && Pref <= previousVWAP_1_5_Down + 5 * sc.TickSize) || (Pref >= previousVWAP_2_0_Up - 5 * sc.TickSize && Pref <= previousVWAP_2_0_Up + 5 * sc.TickSize) || (Pref >= previousVWAP_2_0_Down - 5 * sc.TickSize && Pref <= previousVWAP_2_0_Down + 5 * sc.TickSize)) { scenarioForBuyMet = true; buyScenarioMessage = "Scenario: Buy near important levels"; } // Sell scenarios within +/- 5 ticks of each level if ((Pref >= previousHigh - 5 * sc.TickSize && Pref <= previousHigh + 5 * sc.TickSize) || (Pref >= previousLow - 5 * sc.TickSize && Pref <= previousLow + 5 * sc.TickSize) || (Pref >= previousOpen - 5 * sc.TickSize && Pref <= previousOpen + 5 * sc.TickSize) || (Pref >= previousClose - 5 * sc.TickSize && Pref <= previousClose + 5 * sc.TickSize) || (Pref >= previousHalfback - 5 * sc.TickSize && Pref <= previousHalfback + 5 * sc.TickSize) || (Pref >= currentHigh - 5 * sc.TickSize && Pref <= currentHigh + 5 * sc.TickSize) || (Pref >= currentLow - 5 * sc.TickSize && Pref <= currentLow + 5 * sc.TickSize) || (Pref >= currentOpen - 5 * sc.TickSize && Pref <= currentOpen + 5 * sc.TickSize) || (Pref >= currentHalfback - 5 * sc.TickSize && Pref <= currentHalfback + 5 * sc.TickSize) || (Pref >= currentVWAP - 5 * sc.TickSize && Pref <= currentVWAP + 5 * sc.TickSize) || (Pref >= currentVWAP_0_5_Up - 5 * sc.TickSize && Pref <= currentVWAP_0_5_Up + 5 * sc.TickSize) || (Pref >= currentVWAP_0_5_Down - 5 * sc.TickSize && Pref <= currentVWAP_0_5_Down + 5 * sc.TickSize) || (Pref >= currentVWAP_1_0_Up - 5 * sc.TickSize && Pref <= currentVWAP_1_0_Up + 5 * sc.TickSize) || (Pref >= currentVWAP_1_0_Down - 5 * sc.TickSize && Pref <= currentVWAP_1_0_Down + 5 * sc.TickSize) || (Pref >= currentVWAP_1_5_Up - 5 * sc.TickSize && Pref <= currentVWAP_1_5_Up + 5 * sc.TickSize) || (Pref >= currentVWAP_1_5_Down - 5 * sc.TickSize && Pref <= currentVWAP_1_5_Down + 5 * sc.TickSize) || (Pref >= currentVWAP_2_0_Up - 5 * sc.TickSize && Pref <= currentVWAP_2_0_Up + 5 * sc.TickSize) || (Pref >= currentVWAP_2_0_Down - 5 * sc.TickSize && Pref <= currentVWAP_2_0_Down + 5 * sc.TickSize) || (Pref >= previousVWAP - 5 * sc.TickSize && Pref <= previousVWAP + 5 * sc.TickSize) || (Pref >= previousVWAP_0_5_Up - 5 * sc.TickSize && Pref <= previousVWAP_0_5_Up + 5 * sc.TickSize) || (Pref >= previousVWAP_0_5_Down - 5 * sc.TickSize && Pref <= previousVWAP_0_5_Down + 5 * sc.TickSize) || (Pref >= previousVWAP_1_0_Up - 5 * sc.TickSize && Pref <= previousVWAP_1_0_Up + 5 * sc.TickSize) || (Pref >= previousVWAP_1_0_Down - 5 * sc.TickSize && Pref <= previousVWAP_1_0_Down + 5 * sc.TickSize) || (Pref >= previousVWAP_1_5_Up - 5 * sc.TickSize && Pref <= previousVWAP_1_5_Up + 5 * sc.TickSize) || (Pref >= previousVWAP_1_5_Down - 5 * sc.TickSize && Pref <= previousVWAP_1_5_Down + 5 * sc.TickSize) || (Pref >= previousVWAP_2_0_Up - 5 * sc.TickSize && Pref <= previousVWAP_2_0_Up + 5 * sc.TickSize) || (Pref >= previousVWAP_2_0_Down - 5 * sc.TickSize && Pref <= previousVWAP_2_0_Down + 5 * sc.TickSize)) { scenarioForSellMet = true; sellScenarioMessage = "Scenario: Sell near important levels"; } // Market Depth and Recent Volume Check after scenario check if (scenarioForBuyMet) { // Calculate market depth conditions for (int level = 0; level < numLevelsToCheck; ++level) { if (sc.GetBidMarketDepthEntryAtLevel(depthEntry, level)) { bidMBOSum += depthEntry.NumOrders; bidMarketDepthQuantitySum += depthEntry.Quantity; } } bool marketDepthForBuy = (bidMBOSum > askMBOSum) && (bidMarketDepthQuantitySum > askMarketDepthQuantitySum) && (sc.GetRecentAskVolumeAtPrice(Pref) >= volumeThreshold); // Log relevant details SCString message; message.Format("Buy Scenario Met: %s, BidMBOSum: %d, AskMBOSum: %d, BidMarketDepthQuantitySum: %d, AskMarketDepthQuantitySum: %d, Recent Ask Volume: %u, Recent Bid Volume: %u, Pref: %f", buyScenarioMessage.GetChars(), bidMBOSum, askMBOSum, bidMarketDepthQuantitySum, askMarketDepthQuantitySum, sc.GetRecentAskVolumeAtPrice(Pref), sc.GetRecentBidVolumeAtPrice(Pref), Pref); sc.AddMessageToLog(message, 0); if (marketDepthForBuy) { // Place a buy order s_UseTool tool; tool.Clear(); tool.ChartNumber = sc.ChartNumber; tool.DrawingType = DRAWING_RECTANGLEHIGHLIGHT; tool.AddMethod = UTAM_ADD_OR_ADJUST; tool.BeginValue = sc.Bid - numLevelsToCheck * sc.TickSize; tool.EndValue = sc.Bid + sc.TickSize / 2; tool.BeginDateTime = sc.BaseDateTimeIn[sc.ArraySize - 1]; tool.EndDateTime = sc.BaseDateTimeIn[0]; tool.Color = bidHighlightColor; tool.SecondaryColor = bidHighlightColor; tool.TransparencyLevel = 75; tool.LineWidth = 1; sc.UseTool(tool); if (enableOrderPlacement) { s_SCNewOrder newOrder; newOrder.OrderQuantity = orderQuantity; newOrder.OrderType = SCT_ORDERTYPE_MARKET; newOrder.Stop1Offset = stopLossTicks * sc.TickSize; newOrder.Target1Offset = takeProfitTicks * sc.TickSize; newOrder.AttachedOrderTarget1Type = SCT_ORDERTYPE_LIMIT; newOrder.AttachedOrderStopAllType = SCT_ORDERTYPE_STOP; int InternalOrderID = sc.BuyOrder(newOrder); if (InternalOrderID != 0) { tradeInfo.startTime = std::chrono::steady_clock::now(); tradeInfo.startPrice = sc.Bid; tradeInfo.tradeEntered = true; tradeInfo.positionType = 1; tradeInfo.movedToBreakeven = false; tradeInfo.stopOrderID = newOrder.Stop1InternalOrderID; SCString message; message.Format("Buy order placed at price: %f", tradeInfo.startPrice); sc.AddMessageToLog(message, 0); } } } } else if (scenarioForSellMet) { // Calculate market depth conditions for (int level = 0; level < numLevelsToCheck; ++level) { if (sc.GetAskMarketDepthEntryAtLevel(depthEntry, level)) { askMBOSum += depthEntry.NumOrders; askMarketDepthQuantitySum += depthEntry.Quantity; } } bool marketDepthForSell = (askMBOSum > bidMBOSum) && (askMarketDepthQuantitySum > bidMarketDepthQuantitySum) && (sc.GetRecentBidVolumeAtPrice(Pref) >= volumeThreshold); // Log relevant details SCString message; message.Format("Sell Scenario Met: %s, BidMBOSum: %d, AskMBOSum: %d, BidMarketDepthQuantitySum: %d, AskMarketDepthQuantitySum: %d, Recent Ask Volume: %u, Recent Bid Volume: %u, Pref: %f", sellScenarioMessage.GetChars(), bidMBOSum, askMBOSum, bidMarketDepthQuantitySum, askMarketDepthQuantitySum, sc.GetRecentAskVolumeAtPrice(Pref), sc.GetRecentBidVolumeAtPrice(Pref), Pref); sc.AddMessageToLog(message, 0); if (marketDepthForSell) { // Place a sell order s_UseTool tool; tool.Clear(); tool.ChartNumber = sc.ChartNumber; tool.DrawingType = DRAWING_RECTANGLEHIGHLIGHT; tool.AddMethod = UTAM_ADD_OR_ADJUST; tool.BeginValue = sc.Ask - sc.TickSize / 2; tool.EndValue = sc.Ask + numLevelsToCheck * sc.TickSize; tool.BeginDateTime = sc.BaseDateTimeIn[sc.ArraySize - 1]; tool.EndDateTime = sc.BaseDateTimeIn[0]; tool.Color = askHighlightColor; tool.SecondaryColor = askHighlightColor; tool.TransparencyLevel = 75; tool.LineWidth = 1; sc.UseTool(tool); if (enableOrderPlacement) { s_SCNewOrder newOrder; newOrder.OrderQuantity = orderQuantity; newOrder.OrderType = SCT_ORDERTYPE_MARKET; newOrder.Stop1Offset = stopLossTicks * sc.TickSize; newOrder.Target1Offset = takeProfitTicks * sc.TickSize; newOrder.AttachedOrderTarget1Type = SCT_ORDERTYPE_LIMIT; newOrder.AttachedOrderStopAllType = SCT_ORDERTYPE_STOP; int InternalOrderID = sc.SellOrder(newOrder); if (InternalOrderID != 0) { tradeInfo.startTime = std::chrono::steady_clock::now(); tradeInfo.startPrice = sc.Ask; tradeInfo.tradeEntered = true; tradeInfo.positionType = -1; tradeInfo.movedToBreakeven = false; tradeInfo.stopOrderID = newOrder.Stop1InternalOrderID; SCString message; message.Format("Sell order placed at price: %f", tradeInfo.startPrice); sc.AddMessageToLog(message, 0); } } } } // Monitoring and managing the trade (both buy and sell) if (tradeInfo.tradeEntered) { double currentPrice = sc.Close[sc.ArraySize - 1]; int profitTicks = static_cast<int>((currentPrice - tradeInfo.startPrice) / sc.TickSize) * tradeInfo.positionType; // Move stop to breakeven logic if (!tradeInfo.movedToBreakeven && profitTicks >= trailingStopTriggerTicks) { s_SCNewOrder modifyOrder; modifyOrder.InternalOrderID = tradeInfo.stopOrderID; modifyOrder.Price1 = tradeInfo.startPrice + trailingStopOffsetTicks * sc.TickSize * tradeInfo.positionType; // Adjust stop to breakeven int modifyResult = sc.ModifyOrder(modifyOrder); if (modifyResult == 1) { SCString message; message.Format("Stop moved to breakeven at price: %f", tradeInfo.startPrice); sc.AddMessageToLog(message, 0); tradeInfo.movedToBreakeven = true; } else { SCString message; message.Format("Failed to modify stop order for breakeven. Error: %d", modifyResult); sc.AddMessageToLog(message, 1); } } } } Ty in advance :Dc |
[2024-08-30 16:39:03] |
OctoPi - Posts: 37 |
Looks like your BE logic would never trigger if trade position is short. What's the problem exactly? |
[2024-08-31 09:17:22] |
User357489 - Posts: 72 |
Hi OctoPi So as I've been watching, it opens trades and on the occasions where the trade has gone X amount of ticks into profit, I was expecting the stop order to move to break even. I noticed it worked like maybe once but sporadically. It's not consistent and doesn't trigger the move to break even immediately upon reaching X ticks. Thanks 🙏🏽 |
[2024-08-31 09:56:27] |
User431178 - Posts: 540 |
@User357489 Before delving into your code, I have a question. Is there a reason why you would not use the built in move to breakeven and trailing stop functionality? |
[2024-08-31 10:01:55] |
User357489 - Posts: 72 |
I'm a novice, so may have overlooked it. Started to get overwhelmed so reached out for guidance |
[2024-08-31 10:25:22] |
User431178 - Posts: 540 |
Ok, understood, didn't want to suggest it if you had already discounted it for some reason. There are some examples in SierraChart\ACS_Source\TradingSystem.cpp, I think you'll find it much easier. |
[2024-08-31 11:01:40] |
User357489 - Posts: 72 |
That's alright, appreciate it! Ty I'll try that and go from there 😁 |
[2024-09-01 16:31:09] |
OctoPi - Posts: 37 |
My only point was - your code takes a negative value if the trade is short, which you then compare to positive value setting. Wouldn't work unless you take absolute value. In either scenario, the suggestion above is a better one - use SC built-in stuff. If you want something pre-built that doesn't require coding - give our plugin a whirl. Click/No Code Strategy Builder, Algo Portfolio Composer plugin for Sierra Chart |
To post a message in this thread, you need to log in with your Sierra Chart account: