Support Board
Date/Time: Sun, 15 Jun 2025 15:55:24 +0000
[User Discussion] - Bookmarking chart locations?
View Count: 631
[2021-07-24 12:47:14] |
User299440 - Posts: 1 |
Hi, Is there any way to easily flip between multiple chart locations (price/time)? I've used the 'Goto DtTm' function but I have to keep rentering the two times and scroll up and down as the price level isn't included. Many Thanks! Tim. |
[2025-05-19 21:45:48] |
barucho - Posts: 17 |
I created a custom study to help navigate chart drawings during backtesting, it can be used for a bookmark feature also. Here’s how it works: Setup: Add three Custom Study Buttons to your Control Bar and note their IDs (check Control Bar settings). Add the study to your chart and set the button IDs in the study inputs for Next, Previous, and Refresh actions. In the study’s Drawing Text Keyword field, enter a unique keyword (e.g., “SETUP”). Usage: When adding drawings (arrows, lines, text, etc.) to your chart, set the keyword as the drawing's text (e.g., “SETUP”). Use the Control Bar buttons to: Move forward or backward to drawing locations (chart data must be loaded in order to jump to a drawing at that location). Refresh the internal list after adding new drawings. This is great for quickly reviewing marked setups (or whatever). Source Code: I’m sharing the source code for this study below for community use. Feel free to modify or adapt it as needed. Disclaimer: This custom study and its source code are provided “as is” for educational and personal use. I make no warranties, express or implied, regarding its functionality, accuracy, or suitability for trading. Use at your own risk, and thoroughly test in a simulated environment before applying to live trading. I’m not liable for any losses or damages resulting from its use. Always consult a financial advisor before making trading decisions. #include "sierrachart.h" #include <vector> #include <algorithm> SCDLLName("Drawing Navigator") // Define button identifiers (these correspond to ACS_BUTTON_1, ACS_BUTTON_2, etc.) const int REFRESH_BUTTON = ACS_BUTTON_1; // Button 1 const int NEXT_BUTTON = ACS_BUTTON_2; // Button 2 const int PREV_BUTTON = ACS_BUTTON_3; // Button 3 SCSFExport scsf_DrawingNavigator(SCStudyInterfaceRef sc) { // Set defaults if (sc.SetDefaults) { sc.GraphName = "Drawing Navigator"; sc.AutoLoop = 0; // Manual looping, no bar-by-bar processing needed sc.UpdateAlways = 1; // Ensure study function is called continuously sc.Input[0].Name = "Drawing Text Keyword"; sc.Input[0].SetString(""); // Default empty keywords sc.Input[1].Name = "Refresh Button ID"; sc.Input[1].SetInt(REFRESH_BUTTON); // Default to refresh button ID sc.Input[1].SetIntLimits(0, 255); // Limit to valid button IDs sc.Input[2].Name = "Next Button ID"; sc.Input[2].SetInt(NEXT_BUTTON); // Default to next button ID sc.Input[2].SetIntLimits(0, 255); // Limit to valid button IDs sc.Input[3].Name = "Previous Button ID"; sc.Input[3].SetInt(PREV_BUTTON); // Default to previous button ID sc.Input[3].SetIntLimits(0, 255); // Limit to valid button IDs return; } // Inputs SCInputRef KeywordsInput = sc.Input[0]; SCInputRef RefreshButtonId = sc.Input[1]; SCInputRef NextButtonId = sc.Input[2]; SCInputRef PrevButtonId = sc.Input[3]; // Persistent variables SCString &prevKeywordStr = sc.GetPersistentSCString(1); // Previous keyword string for change detection std::vector<SCDateTime> *&anchorList = reinterpret_cast<std::vector<SCDateTime> *&>(sc.GetPersistentPointer(0)); // List of anchor date-times int &needsRefresh = sc.GetPersistentInt(3); // Flag to trigger refresh // Initialize anchor list if not already done if (anchorList == NULL) { anchorList = new std::vector<SCDateTime>(); sc.SetPersistentPointer(0, anchorList); } // Set button text and tooltips (do this only once, e.g., on first call) if (sc.Index == 0) { sc.SetCustomStudyControlBarButtonText(1, "Refresh"); sc.SetCustomStudyControlBarButtonHoverText(1, "Refresh the anchor list"); sc.SetCustomStudyControlBarButtonText(2, "Next"); sc.SetCustomStudyControlBarButtonHoverText(2, "Navigate to the next anchor"); sc.SetCustomStudyControlBarButtonText(3, "Previous"); sc.SetCustomStudyControlBarButtonHoverText(3, "Navigate to the previous anchor"); } // Handle button clicks if (sc.PointerEventType == SC_ACS_BUTTON_ON) { if (sc.MenuEventID == RefreshButtonId.GetInt()) { needsRefresh = 1; // Set flag to rebuild anchor list sc.SetCustomStudyControlBarButtonEnable(RefreshButtonId.GetInt(), 0); // Turn off the button } else if (sc.MenuEventID == NextButtonId.GetInt()) { if (!anchorList->empty()) { int lastVisibleIndex = sc.IndexOfLastVisibleBar; if (lastVisibleIndex >= 0 && lastVisibleIndex < sc.ArraySize) { SCDateTime visibleEndDT = sc.BaseDateTimeIn[lastVisibleIndex]; auto it = std::upper_bound(anchorList->begin(), anchorList->end(), visibleEndDT); if (it != anchorList->end()) { SCDateTime dt = *it; sc.ScrollToDateTime = dt; SCString msg; msg.Format("Jumped to next anchor at %s", sc.DateTimeToString(dt, 0).GetChars()); sc.AddMessageToLog(msg, 1); } else { // Wrap around to the first anchor SCDateTime dt = anchorList->front(); sc.ScrollToDateTime = dt; SCString msg; msg.Format("Wrapped to first anchor at %s", sc.DateTimeToString(dt, 0).GetChars()); sc.AddMessageToLog(msg, 1); } sc.ReCenterConstantRangeScale(sc.ChartNumber); } else { sc.AddMessageToLog("Invalid last visible bar index.", 1); } } else { sc.AddMessageToLog("No anchors to navigate.", 1); } sc.SetCustomStudyControlBarButtonEnable(NextButtonId.GetInt(), 0); // Turn off the button } else if (sc.MenuEventID == PrevButtonId.GetInt()) { if (!anchorList->empty()) { int firstVisibleIndex = sc.IndexOfLastVisibleBar; if (firstVisibleIndex >= 0 && firstVisibleIndex < sc.ArraySize) { SCDateTime visibleStartDT = sc.BaseDateTimeIn[firstVisibleIndex]; auto it = std::lower_bound(anchorList->begin(), anchorList->end(), visibleStartDT); if (it != anchorList->begin()) { --it; SCDateTime dt = *it; sc.ScrollToDateTime = dt; SCString msg; msg.Format("Jumped to previous anchor at %s", sc.DateTimeToString(dt, 0).GetChars()); sc.AddMessageToLog(msg, 1); } else { // Wrap around to the last anchor SCDateTime dt = anchorList->back(); sc.ScrollToDateTime = dt; SCString msg; msg.Format("Wrapped to last anchor at %s", sc.DateTimeToString(dt, 0).GetChars()); sc.AddMessageToLog(msg, 1); } sc.ReCenterConstantRangeScale(sc.ChartNumber); } else { sc.AddMessageToLog("Invalid first visible bar index.", 1); } } else { sc.AddMessageToLog("No anchors to navigate.", 1); } sc.SetCustomStudyControlBarButtonEnable(PrevButtonId.GetInt(), 0); // Turn off the button } } // Get current keyword string SCString keywordStr = KeywordsInput.GetString(); // Rebuild anchor list if keywords changed or refresh requested if (keywordStr != prevKeywordStr || needsRefresh) { anchorList->clear(); int drawingIndex = 0; s_UseTool drawing; while (sc.GetUserDrawnChartDrawing(sc.ChartNumber, DRAWING_UNKNOWN, drawing, drawingIndex)) { if (drawing.Text.CompareNoCase(keywordStr) == 0) { SCDateTime dt = drawing.EndDateTime; anchorList->push_back(dt); } drawingIndex++; } std::sort(anchorList->begin(), anchorList->end()); // Sort chronologically prevKeywordStr = keywordStr; needsRefresh = 0; // Reset the refresh flag if (anchorList->empty()) { sc.AddMessageToLog("No drawings found with the specified keywords.", 1); } else { SCString msg; msg.Format("Found %u drawings with specified keywords.", static_cast<unsigned int>(anchorList->size())); sc.AddMessageToLog(msg, 1); } } // Clean up when the study is removed if (sc.LastCallToFunction) { if (anchorList != NULL) { delete anchorList; sc.SetPersistentPointer(0, NULL); } } } Date Time Of Last Edit: 2025-05-19 21:50:13
|
To post a message in this thread, you need to log in with your Sierra Chart account: