
MQL5 Coobook: 지표 하위 창 컨트롤 - 스크롤바
소개
계속해서 다양한 컨트롤을 탐색하고 이번에는 스크롤바에 주의를 기울이겠습니다. 이전 글 "MQL5 Cookbook: Indicator Subwindow Controls - Buttons"에서와 같이 지표 하위 창에서 작업할 것입니다. OnChartEvent() 함수의 이벤트 작업에 대한 자세한 설명을 제공하는 위에서 언급한 글을 잠시 읽어보십시오. 이 점은 이 글에서 가볍게 다룰 것입니다. 설명을 위해 이번에는 MQL5 리소스를 사용하여 얻을 수 있는 모든 금융 상품 속성의 큰 목록에 대한 세로 스크롤 바를 만듭니다.
MQL5 프로그래밍에 대한 이전 글에서는 그래픽 개체 OBJ_LABEL(텍스트 레이블)를 사용하여 목록을 생성했습니다. 이 글에서는 캔버스를 사용하여 텍스트를 표시합니다. 이러한 접근 방식의 편리함은 많은 수의 OBJ_LABEL 개체 대신 OBJ_BITMAP_LABEL(비트맵 레이블) 하나만 사용한다는 것입니다. 캔버스에 모든 인터페이스 요소를 그릴 수 있지만 이번에는 텍스트로만 제한하겠습니다.
스크롤바는 매우 간단합니다. 일반적으로 화살표 버튼이 있지만 우리의 경우에는 기능이 아닙니다. 스크롤바는 배경과 스크롤 상자로만 구성됩니다. 커서가 스크롤 상자 위로 이동하면 스크롤 상자의 색상이 변경됩니다. 클릭하면 색상이 변경되어 사용자에게 이제 스크롤 상자가 선택되었으며 끌 수 있음을 알립니다. 스크롤 개체를 만들 때 OBJ_RECTANGLE_LABEL(사각형 레이블) 유형의 그래픽 개체를 사용합니다.
지표 개발
프로그래밍을 시작합시다. 이전 글에서 했던 것처럼 지표의 템플릿을 만듭니다. 맨 처음에는 평소와 같이 변수와 배열을 선언해야 합니다. 캔버스로 작업할 수 있도록 표준 라이브러리의 CCanvas 클래스를 포함합니다.
#define LIST_SIZE 71 // Number of strings in the list of financial instrument properties //--- Include the class for working with the canvas #include <Canvas\Canvas.mqh> CCanvas canvas; //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ //--- Indicator subwindow properties int subwindow_number =WRONG_VALUE; // Subwindow number int subwindow_height =0; // Subwindow height string subwindow_shortname ="TestScrollbar"; // Short name of the indicator string prefix =subwindow_shortname+"_"; // Prefix for objects int chart_width =0; // Chart width int chart_height =0; // Chart height int chart_y_offset =0; // Distance from the chart top to the subwindow //--- Canvas properties string canvas_name =prefix+"canvas"; // Canvas name color canvas_background_color =C'20,20,20'; // Canvas background color ENUM_COLOR_FORMAT color_format =COLOR_FORMAT_XRGB_NOALPHA; // Alpha channel component is ignored //--- List properties int list_height =0; // List height int text_height =0; // Text height int font_size =15; // Font size string font_name ="Calibri"; // Font double line_size =100/LIST_SIZE; // Size of a single string on the list, expressed as percentage //--- Scrollbar properties: scroll box string scrollbar_thumb_name =prefix+"scrollbar_thumb"; // Scroll box object name int scrollbar_thumb_x1 =0; // x1 coordinate int scrollbar_thumb_y1 =0; // y1 coordinate int scrollbar_thumb_x2 =0; // x2 coordinate int scrollbar_thumb_y2 =0; // y2 coordinate double scrollbar_thumb_y_percent =0.0; // Y-coordinate expressed as percentage int scrollbar_thumb_width =9; // Width int scrollbar_thumb_height =0; // Height bool scrollbar_thumb_clicked =false; // State (whether or not the scroll box is clicked) color scrollbar_thumb_color =clrSilver; // Scroll box color color scrollbar_thumb_color_on_hover=clrDimGray; // Scroll box color when the cursor hovers over it color scrollbar_thumb_color_on_click=clrSlateGray; // Scroll box color when clicked //--- Scrollbar properties: background string scrollbar_background_name =prefix+"scrollbar_background"; // Background object name int scrollbar_background_width =9; // Background width color scrollbar_background_color =C'50,50,50'; // Background color //--- Scrollbar properties: other int scrollbar_fix_point =0; // Y-coordinate of the fix point upon clicking int scrollbar_fix_point_y_offest =0; // Distance along the Y-axis from the scrollbar top to the fix point //--- Mouse button state (pressed/released) bool mouse_button_state=false; //--- Arrays for financial instrument properties color symbol_property_colors[]; // Colors of values string symbol_property_values[]; // Values //--- Financial instrument property names string symbol_propety_names[LIST_SIZE]= { "Number of deals in the current session", "Total number of Buy orders at the moment", "Total number of Sell orders at the moment", "Volume of the last deal", "Maximum daily volume", "Minimum daily volume", "Time of the last quote", "Number of decimal places", "Spread in points", "Floating spread indication", "Maximum number of requests displayed in the Depth of Market", "Contract price calculation mode", "Order execution type", "Trading start date for an instrument (usually used for futures)", "Trading end date for an instrument (usually used for futures)", "Minimum distance in points from the current closing price for the purpose of setting Stop orders", "Freeze distance for trading operations (in points)", "Deal execution mode", "Swap calculation model", "Day of the week when triple swap is charged", "Flags of allowed order expiration modes", "Flags of allowed order filling modes", //--- "Bid - best price at which an instrument can be sold", "Maximum Bid of the day", "Minimum Bid of the day", "Ask - best price at which an instrument can be bought", "Maximum Ask of the day", "Minimum Ask of the day", "Last - last deal price", "Maximum Last of the day", "Minimum Last of the day", "Point value", "Calculated tick value for a winning position", "Calculated tick value for a losing position", "Minimum price change", "Trade contract size", "Minimum volume for deal execution", "Maximum volume for deal execution", "Minimum step of volume change for deal execution", "Maximum allowable total volume of an open position and pending orders in the same direction", "Long swap value", "Short swap value", "Initial margin - amount in the margin currency required for opening a position (1 lot)", "Maintenance margin for an instrument", "Margin requirement applicable to long positions", "Margin requirement applicable to short positions", "Margin requirement applicable to Limit orders", "Margin requirement applicable to Stop orders", "Margin requirement applicable to Stop Limit orders", "Total volume of deals in the current session", "Total turnover in the current session", "Total volume of open positions", "Total volume of buy orders at the moment", "Total volume of sell orders at the moment", "Open price of the session", "Close price of the session", "Average weighted price of the session", "Settlement price of the current session", "Minimum allowable price value for the session", "Maximum allowable price value for the session", //--- "Base currency of an instrument", "Profit currency", "Margin currency", "Current quote source", "String description of a symbol", "Name of a trading symbol in the international system of securities identification numbers (ISIN)", "Location in the symbol tree", //--- "Current number of bars for a symbol on a selected time frame", "The very first date for a symbol on a selected time frame", "The very first date in the history for a symbol on a selected time frame", "Symbol data synchronized" };
먼저 캔버스에 속성 목록을 표시하는 데 필요한 모든 기능을 작성해 보겠습니다. 이 작업이 완료되면 스크롤바 생성을 진행합니다.
캔버스를 생성하기 위해 AddCanvas() 함수를 작성하고 CCanvas 클래스의 CreateBitmapLabel() 메소드의 두 번째 변형을 사용합니다.
//+------------------------------------------------------------------+ //| Adding canvas | //+------------------------------------------------------------------+ void AddCanvas() { //--- If there is no canvas, add it if(ObjectFind(0,canvas_name)<0) canvas.CreateBitmapLabel(0,subwindow_number,canvas_name,0,0,chart_width,subwindow_height,color_format); }
이를 위해 CCanvas 클래스에서 Resize() 를 사용하는 ResizeCanvas() 함수를 작성합니다. 이를 위해 CCanvas 클래스에서 Resize() 를 사용하는 ResizeCanvas() 함수를 작성합니다.
//+------------------------------------------------------------------+ //| Resizing canvas | //+------------------------------------------------------------------+ void ResizeCanvas() { //--- If the canvas has already been added to the indicator subwindow, set the new size if(ObjectFind(0,canvas_name)==subwindow_number) canvas.Resize(chart_width,subwindow_height); //--- If there is no canvas, add it else canvas.CreateBitmapLabel(0,subwindow_number,canvas_name,0,0,chart_width,subwindow_height,color_format); }
캔버스를 삭제하려면 Destroy() 를 사용합니다.
//+------------------------------------------------------------------+ //| Deleting canvas | //+------------------------------------------------------------------+ void DeleteCanvas() { if(ObjectFind(0,canvas_name)>0) canvas.Destroy(); }
이 글에서 우리는 또한 글꼴 설정을 위한 FontSet(), 글자 높이를 결정하기 위한TextHeight(), 캔버스에 텍스트를 프린트하기 위한 TextOut(), 캔버스를 지우기 위한 Erase() 그리고 다시 그리기를 위한 Update()와 같은 CCanvas 클래스의 다른 메소드를 사용할 것입니다. 더 아래에서 위의 방법이 프로그램에서 어디에 사용되는지 볼 것입니다.
OnInit() 함수에서 초기화하는 동안 프로그램 작업을 위한 길을 열어야 합니다. 아래 코드는 수행해야 할 작업을 보여줍니다. 각 문자열에 제공된 주석은 작업을 더 잘 이해하는 데 도움이 됩니다. CCanvas 클래스의 FontSet() 및 TextHeight() 는 프로그램의 이 부분에서만 사용됩니다.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Enable tracking of mouse events ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true); //--- Set the short name IndicatorSetString(INDICATOR_SHORTNAME,subwindow_shortname); //--- Set sizes of arrays of symbol properties and their colors ArrayResize(symbol_property_colors,LIST_SIZE); ArrayResize(symbol_property_values,LIST_SIZE); //--- Set subwindow properties SetSubwindowProperties(); //--- Set the font for displaying on the canvas canvas.FontSet(font_name,font_size,FW_NORMAL); //--- Save the text size (height) for calculations text_height=canvas.TextHeight("A")-1; //--- Calculate the height of the entire list list_height=text_height*LIST_SIZE; //--- Add the canvas to the chart AddCanvas(); //--- Display the list of symbol properties ShowSymbolInfo(); //--- Refresh the chart ChartRedraw(); //--- Everything completed successfully return(INIT_SUCCEEDED); }
SetSubwindowProperties() 함수는 이전 문서에서 가져온 것입니다. 이 함수는 지표 하위 창 번호와 크기를 전역 변수에 할당합니다. ShowSymbolInfo() 함수로 바로 진행해 보겠습니다.
//+------------------------------------------------------------------+ //| Displaying current symbol information | //+------------------------------------------------------------------+ void ShowSymbolInfo(double current_thumb_position=0.0) { int list_lines =0; // Counter of strings displayed in the canvas double thumb_position =0.0; // Position of the scroll box expressed as percentage for determining the first displayed string int y_distance =0; // For determining the coordinate of the next string in the list int line_number =0; // Number of the string starting from which the list will be displayed //--- Determine the string starting from which the list will be displayed for(int i=0; i<LIST_SIZE; i++) { //--- Count strings until you reach the one starting from which the list will be displayed if(thumb_position>=current_thumb_position) break; //--- thumb_position+=line_size; line_number++; } //--- Initialize list arrays from the specified string InitializePropertyArrays(line_number); //--- Clear the canvas canvas.Erase(canvas_background_color); //--- Show the list on the canvas for(int i=line_number; i<LIST_SIZE; i++) { //--- Property name canvas.TextOut(655,y_distance,symbol_propety_names[i]+" :",ColorToARGB(clrWhite),TA_RIGHT|TA_TOP); //--- Property value canvas.TextOut(665,y_distance,symbol_property_values[i],ColorToARGB(symbol_property_colors[i]),TA_LEFT|TA_TOP); //--- Calculate the coordinate for the next string y_distance+=text_height; //--- Count the number of displayed strings list_lines++; //--- If you go beyond the subwindow boundaries, terminate the loop if(list_lines*text_height>subwindow_height) break; } //--- Refresh the canvas canvas.Update(); }
ShowSymbolInfo() 함수에는 기본적으로 0과 같은 current_thumb_position 매개변수가 하나 있습니다(기본값을 사용해야 하는 경우 함수에 대한 값). 이 매개변수는 목록이 표시되어야 하는 문자열을 결정합니다. 즉, 0 값은 목록이 맨 처음부터 표시되어야 함을 의미합니다.
맨 처음에 목록이 표시되어야 하는 문자열의 번호를 결정합니다. 그런 다음 값 및 색상 배열(위 코드에서 강조 표시된 문자열)이 InitializePropertyArrays() 함수에서 초기화됩니다. 초기화는 이전 루프에서 결정된 문자열부터 수행됩니다. 그런 다음 Erase() 를 사용하여 캔버스를 지운 후 실제로 전체 캔버스를 지정된 색상으로 채웁니다. 마지막 루프에서 텍스트는 TextOut()를 사용하여 캔버스에 프린트됩니다. 마지막에 Update() 를 사용하여 캔버스를 새로 고칩니다.
InitializePropertyArrays() 함수 코드는 다음과 같습니다.
//+-----------------------------------------------------------------------+ //| Initializing arrays of values and their colors for the current symbol | //+-----------------------------------------------------------------------+ void InitializePropertyArrays(int line_number) { int lines_count=0; //--- for(int i=line_number; i<LIST_SIZE; i++) { //--- Determine the value and color of the symbol property symbol_property_values[i]=GetStringSymbolInfoByIndex(i); symbol_property_colors[i]=GetColorSymbolInfoByIndex(i); //--- Increase the counter lines_count++; //--- If the number of strings exceeds the subwindow height, exit if(lines_count*text_height>subwindow_height) break; } }
위의 코드는 GetStringSymbolInfoByIndex() 및 GetColorSymbolInfoByIndex()라는 유사한 원칙에 따라 작동하는 두 가지 함수를 사용하여 기호 속성의 값과 해당 색상을 결정한다고 제안합니다.
GetStringSymbolInfoByIndex() 함수는 단순하지만 많은 속성으로 인해 상당히 방대합니다. 게다가 일부 속성을 얻으려면 보조 기능이 필요합니다(아래 코드에서 강조 표시된 문자열).
//+------------------------------------------------------------------+ //| Returning the string value of the symbol property by index | //+------------------------------------------------------------------+ string GetStringSymbolInfoByIndex(int index) { string str ="-"; long l_check_value =0; double d_check_value =0.0; string s_check_value =""; //--- switch(index) { case 0 : l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_SESSION_DEALS); str=(l_check_value==0) ? "-" : IntegerToString(l_check_value); break; case 1 : l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_SESSION_BUY_ORDERS); str=(l_check_value==0) ? "-" : IntegerToString(l_check_value); break; case 2 : l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_SESSION_SELL_ORDERS); str=(l_check_value==0) ? "-" : IntegerToString(l_check_value); break; case 3 : l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_VOLUME); str=(l_check_value==0) ? "-" : IntegerToString(l_check_value); break; case 4 : l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_VOLUMEHIGH); str=(l_check_value==0) ? "-" : IntegerToString(l_check_value); break; case 5 : l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_VOLUMELOW); str=(l_check_value==0) ? "-" : IntegerToString(l_check_value); break; case 6 : l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_TIME); str=(l_check_value==0) ? "-" : TimeToString(l_check_value); break; case 7 : str=IntegerToString(SymbolInfoInteger(_Symbol,SYMBOL_DIGITS)); break; case 8 : str=IntegerToString(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD)); break; case 9 : str=(!SymbolInfoInteger(_Symbol,SYMBOL_SPREAD_FLOAT)) ? "false" : "true"; break; case 10 : l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_TICKS_BOOKDEPTH); str=(l_check_value==0) ? "-" : DoubleToString(l_check_value,_Digits); break; case 11 : str=TradeCalcModeToString(SymbolInfoInteger(_Symbol,SYMBOL_TRADE_CALC_MODE)); break; case 12 : str=TradeModeToString(SymbolInfoInteger(_Symbol,SYMBOL_TRADE_MODE)); break; case 13 : l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_START_TIME); str=(l_check_value==0) ? "-" : TimeToString(l_check_value); break; case 14 : l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_EXPIRATION_TIME); str=(l_check_value==0) ? "-" : TimeToString(l_check_value); break; case 15 : l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); str=(l_check_value==0) ? "false" : IntegerToString(l_check_value); break; case 16 : l_check_value=SymbolInfoInteger(_Symbol,SYMBOL_TRADE_FREEZE_LEVEL); str=(l_check_value==0) ? "false" : IntegerToString(l_check_value); break; case 17 : str=TradeExeModeToString(SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE)); break; case 18 : str=SwapModeToString(SymbolInfoInteger(_Symbol,SYMBOL_SWAP_MODE)); break; case 19 : str=WeekdayToString(SymbolInfoInteger(_Symbol,SYMBOL_SWAP_ROLLOVER3DAYS)); break; case 20 : str=ExpirationModeToString(); break; case 21 : str=FillingModeToString(); break; //--- case 22 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_BID); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits); break; case 23 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_BIDHIGH); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits); break; case 24 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_BIDLOW); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits); break; case 25 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_ASK); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits); break; case 26 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_ASKHIGH); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits); break; case 27 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_ASKLOW); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits); break; case 28 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_LAST); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits); break; case 29 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_LASTHIGH); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits); break; case 30 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_LASTLOW); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,_Digits); break; case 31 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_POINT),_Digits); break; case 32 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE_PROFIT),2); break; case 33 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE_LOSS),2); break; case 34 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE),_Digits); break; case 35 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_TRADE_CONTRACT_SIZE),2); break; case 36 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN),2); break; case 37 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX),2); break; case 38 : str=DoubleToString(SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP),2); break; case 39 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT); str=(d_check_value==0) ? "Unlimited" : DoubleToString(d_check_value,2); break; case 40 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SWAP_LONG); str=(d_check_value==0) ? "false" : DoubleToString(d_check_value,2); break; case 41 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SWAP_SHORT); str=(d_check_value==0) ? "false" : DoubleToString(d_check_value,2); break; case 42 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_INITIAL); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 43 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_MAINTENANCE); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 44 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_LONG); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 45 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_SHORT); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 46 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_LIMIT); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 47 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_STOP); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 48 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_MARGIN_STOPLIMIT); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 49 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_VOLUME); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 50 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_TURNOVER); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 51 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_INTEREST); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 52 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_BUY_ORDERS_VOLUME); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 53 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_SELL_ORDERS_VOLUME); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 54 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_OPEN); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 55 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_CLOSE); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 56 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_AW); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 57 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_PRICE_SETTLEMENT); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 58 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_PRICE_LIMIT_MIN); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; case 59 : d_check_value=SymbolInfoDouble(_Symbol,SYMBOL_SESSION_PRICE_LIMIT_MAX); str=(d_check_value==0) ? "-" : DoubleToString(d_check_value,2); break; //--- case 60 : str=SymbolInfoString(_Symbol,SYMBOL_CURRENCY_BASE); break; case 61 : str=SymbolInfoString(_Symbol,SYMBOL_CURRENCY_PROFIT); break; case 62 : str=SymbolInfoString(_Symbol,SYMBOL_CURRENCY_MARGIN); break; case 63 : s_check_value=SymbolInfoString(_Symbol,SYMBOL_BANK); str=(s_check_value!="") ? s_check_value : "-"; break; case 64 : str=SymbolInfoString(_Symbol,SYMBOL_DESCRIPTION); break; case 65 : s_check_value=SymbolInfoString(_Symbol,SYMBOL_ISIN); str=(s_check_value!="") ? s_check_value : "-"; break; case 66 : str=SymbolInfoString(_Symbol,SYMBOL_PATH); break; //--- case 67 : str=IntegerToString(SeriesInfoInteger(_Symbol,_Period,SERIES_BARS_COUNT)); break; case 68 : str=TimeToString((datetime)SeriesInfoInteger(_Symbol,_Period,SERIES_FIRSTDATE)); break; case 69 : str=TimeToString((datetime)SeriesInfoInteger(_Symbol,_Period,SERIES_SERVER_FIRSTDATE)); break; case 70 : str=(!(bool)SeriesInfoInteger(_Symbol,_Period,SERIES_SYNCHRONIZED)) ? "false" : "true"; break; } //--- return(str); }
위에서 강조 표시된 기능: TradeCalcModeToString(), TradeModeToString(), TradeExeModeToString(), SwapModeToString() 및 WeekdayToString()은 전달된 값에 따라 속성의 문자열 표현을 반환합니다.
//+------------------------------------------------------------------+ //| Returning string representation of the margin calculation | //| method for an instrument | //+------------------------------------------------------------------+ string TradeCalcModeToString(long mode) { string str="?"; //--- switch((ENUM_SYMBOL_CALC_MODE)mode) { case SYMBOL_CALC_MODE_FOREX : str="Forex mode"; break; case SYMBOL_CALC_MODE_FUTURES : str="Futures mode"; break; case SYMBOL_CALC_MODE_CFD : str="CFD mode"; break; case SYMBOL_CALC_MODE_CFDINDEX : str="CFD index mode"; break; case SYMBOL_CALC_MODE_CFDLEVERAGE : str="CFD Leverage mode"; break; case SYMBOL_CALC_MODE_EXCH_STOCKS : str="Exchange mode"; break; case SYMBOL_CALC_MODE_EXCH_FUTURES : str="Futures mode"; break; case SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS : str="FORTS Futures mode"; break; } //--- return(str); } //+------------------------------------------------------------------+ //| Returning string representation of the trade mode | //+------------------------------------------------------------------+ string TradeModeToString(long mode) { string str="-"; //--- switch((ENUM_SYMBOL_TRADE_MODE)mode) { case SYMBOL_TRADE_MODE_DISABLED : str="Trade is disabled for a given symbol"; break; case SYMBOL_TRADE_MODE_LONGONLY : str="Only long positions are allowed"; break; case SYMBOL_TRADE_MODE_SHORTONLY : str="Only short positions are allowed"; break; case SYMBOL_TRADE_MODE_CLOSEONLY : str="Only position closing operations are allowed"; break; case SYMBOL_TRADE_MODE_FULL : str="No trade restrictions"; break; } //--- return(str); } //+------------------------------------------------------------------+ //| Returning string representation of the deal execution mode | //+------------------------------------------------------------------+ string TradeExeModeToString(long mode) { string str="-"; //--- switch((ENUM_SYMBOL_TRADE_EXECUTION)mode) { case SYMBOL_TRADE_EXECUTION_REQUEST : str="Request execution"; break; case SYMBOL_TRADE_EXECUTION_INSTANT : str="Instant execution"; break; case SYMBOL_TRADE_EXECUTION_MARKET : str="Market execution"; break; case SYMBOL_TRADE_EXECUTION_EXCHANGE : str="Exchange execution"; break; } //--- return(str); } //+------------------------------------------------------------------+ //| Returning string representation of the swap calculation model | //+------------------------------------------------------------------+ string SwapModeToString(long mode) { string str="-"; //--- switch((ENUM_SYMBOL_SWAP_MODE)mode) { case SYMBOL_SWAP_MODE_DISABLED : str="No swaps"; break; case SYMBOL_SWAP_MODE_POINTS : str="Swaps calculated in points"; break; case SYMBOL_SWAP_MODE_CURRENCY_SYMBOL : str="Swaps calculated in base currency of the symbol"; break; case SYMBOL_SWAP_MODE_CURRENCY_MARGIN : str="Swaps calculated in margin currency of the symbol"; break; case SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT : str="Swaps calculated in the client's deposit currency"; break; case SYMBOL_SWAP_MODE_INTEREST_CURRENT : str="Swaps expressed as a percent per annum of the instrument price"; break; case SYMBOL_SWAP_MODE_INTEREST_OPEN : str="Swaps expressed as a percent per annum of the position opening price"; break; case SYMBOL_SWAP_MODE_REOPEN_CURRENT : str="Swaps based on position reopening (close price +/-)"; break; case SYMBOL_SWAP_MODE_REOPEN_BID : str="Swaps based on position reopening (bid price +/-)"; break; } //--- return(str); } //+------------------------------------------------------------------+ //| Returning string representation of the day of the week | //+------------------------------------------------------------------+ string WeekdayToString(long day) { string str="-"; //--- switch((ENUM_DAY_OF_WEEK)day) { case SUNDAY : str="Sunday"; break; case MONDAY : str="Monday"; break; case TUESDAY : str="Tuesday"; break; case WEDNESDAY : str="Wednesday"; break; case THURSDAY : str="Thursday"; break; case FRIDAY : str="Friday"; break; case SATURDAY : str="Saturday"; break; } //--- return(str); }
GetStringExpirationMode() 및 GetStringFillingMode() 함수에서 문자열 표현은 현재 기호에 사용할 수 있는 주문 만료 및 볼륨 채우기 모드에 따라 생성됩니다.
//+------------------------------------------------------------------+ //| Returning string representation of the order expiration modes | //+------------------------------------------------------------------+ string ExpirationModeToString() { string str=""; // For string generation //--- Variables for checking the modes bool gtc =false; // The order is valid for an unlimited time until explicitly canceled bool day =false; // The order is valid until the end of the day bool specified =false; // The expiration time is specified in the order bool specified_day =false; // The expiration date is specified in the order //--- Check the modes gtc =IsExpirationTypeAllowed(_Symbol,SYMBOL_EXPIRATION_GTC); day =IsExpirationTypeAllowed(_Symbol,SYMBOL_EXPIRATION_DAY); specified =IsExpirationTypeAllowed(_Symbol,SYMBOL_EXPIRATION_SPECIFIED); specified_day =IsExpirationTypeAllowed(_Symbol,SYMBOL_EXPIRATION_SPECIFIED_DAY); //--- Generate a string of the modes available if(gtc) { StringAdd(str,"GTC"); if(day || specified || specified_day) StringAdd(str," / "); } //--- if(day) { StringAdd(str,"Day"); if(specified || specified_day) StringAdd(str," / "); } //--- if(specified) { StringAdd(str,"Specified"); if(specified_day) StringAdd(str," / "); } //--- if(specified_day) StringAdd(str,"Specified Day"); //--- return(str); } //+------------------------------------------------------------------+ //| Returning string representation of the volume filling modes | //+------------------------------------------------------------------+ string FillingModeToString() { //--- Variable for string generation string str=""; //--- Variables for checking the modes: // "Fill or Kill" - if the required order volume cannot be filled at the specified price, // the order is canceled and the deal is not executed bool fok=false; // "Immediate or Cancel" - if the deal volume can only be partially filled at the price specified in the order, // the deal is executed to the extent of the volume available. The remaining volume of the order is canceled // and the new order is not placed bool ioc=false; // "Return" - The deal is executed to the extent of the volume available at the price specified in the order. // A new order is placed for the remaining volume at the same price bool return_remainder=false; //--- Check the modes fok =IsFillingTypeAllowed(_Symbol,SYMBOL_FILLING_FOK); ioc =IsFillingTypeAllowed(_Symbol,SYMBOL_FILLING_IOC); //--- For "Market execution" and "Exchange execution" modes ENUM_SYMBOL_TRADE_EXECUTION symbol_trade_exemode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE); return_remainder=(symbol_trade_exemode==SYMBOL_TRADE_EXECUTION_MARKET || symbol_trade_exemode==SYMBOL_TRADE_EXECUTION_EXCHANGE) ? true : false; //--- Generate a string of the modes available if(fok) { StringAdd(str,"Fill or Kill"); if(ioc || return_remainder) StringAdd(str," / "); } //--- if(ioc) { StringAdd(str,"Immediate or Cancel"); if(return_remainder) StringAdd(str," / "); } //--- if(return_remainder) StringAdd(str,"Return"); //--- return(str); }
각 모드의 가용성을 개별적으로 확인해야 하므로 편의를 위해 문서 예제에 제공된 IsExpirationTypeAllowed() 및 IsFillingTypeAllowed() 보조 함수를 사용합니다.
//+------------------------------------------------------------------+ //| Checking if a given expiration mode is allowed | //+------------------------------------------------------------------+ bool IsExpirationTypeAllowed(string symbol, int exp_type) { //--- Get the value of the property describing the allowable expiration modes int expiration=(int)SymbolInfoInteger(symbol,SYMBOL_EXPIRATION_MODE); //--- Return true if the exp_type mode is allowed return((expiration&exp_type)==exp_type); } //+------------------------------------------------------------------+ //| Checking if a given filling mode is allowed | //+------------------------------------------------------------------+ bool IsFillingTypeAllowed(string symbol, int fill_type) { //--- Get the value of the property describing the filling mode int filling=(int)SymbolInfoInteger(symbol,SYMBOL_FILLING_MODE); //--- Return true if the fill_type mode is allowed return((filling&fill_type)==fill_type); }
그래서 기호 속성의 문자열 값을 검토했습니다. 이제 GetColorSymbolInfoByIndex() 함수를 살펴보겠습니다. 모든 속성이 표시된 값에 의존하지 않는다는 사실 때문에 이 함수의 코드는 훨씬 간단합니다.
//+------------------------------------------------------------------+ //| Returning the symbol property color by index | //+------------------------------------------------------------------+ color GetColorSymbolInfoByIndex(int index) { double check_value =0.0; color clr =clrWhiteSmoke; //--- switch(index) { case 6 : clr=(SymbolInfoInteger(_Symbol,SYMBOL_TIME)>0) ? clrCornflowerBlue : clrWhiteSmoke; break; //--- case 9 : clr=(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD_FLOAT)>0) ? clrGold : clrRed; break; //--- case 13 : clr=(SymbolInfoInteger(_Symbol,SYMBOL_START_TIME)>0) ? clrCornflowerBlue : clrWhiteSmoke; break; case 14 : clr=(SymbolInfoInteger(_Symbol,SYMBOL_EXPIRATION_TIME)>0) ? clrCornflowerBlue : clrWhiteSmoke; break; //--- case 15 : clr=(SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)>0) ? clrWhiteSmoke : clrRed; break; case 16 : clr=(SymbolInfoInteger(_Symbol,SYMBOL_TRADE_FREEZE_LEVEL)>0) ? clrWhiteSmoke : clrRed; break; //--- case 20 : clr=clrGold; break; case 21 : clr=clrGold; break; //--- case 39 : clr=(SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT)>0) ? clrWhiteSmoke : clrGold; break; case 40 : clr=(SymbolInfoDouble(_Symbol,SYMBOL_SWAP_LONG)>0) ? clrLime : clrRed; break; case 41 : clr=(SymbolInfoDouble(_Symbol,SYMBOL_SWAP_SHORT)>0) ? clrLime : clrRed; break; //--- case 60 : clr=clrGold; break; case 61 : clr=clrGold; break; case 62 : clr=clrGold; break; //--- case 68 : clr=(SeriesInfoInteger(_Symbol,_Period,SERIES_FIRSTDATE)>0) ? clrCornflowerBlue : clrWhiteSmoke; break; case 69 : clr=(SeriesInfoInteger(_Symbol,_Period,SERIES_SERVER_FIRSTDATE)>0) ? clrCornflowerBlue : clrWhiteSmoke; break; case 70 : clr=(!(bool)SeriesInfoInteger(_Symbol,_Period,SERIES_SYNCHRONIZED)) ? clrRed : clrGold; break; } //--- return(clr); }
이제 지표를 컴파일하여 차트에 추가하면 아래 스크린샷과 같이 하위 창에서 기호 속성 목록을 볼 수 있습니다.
그림 1. 스크롤바 없이 차트에 부착된 지표
이 모든 것이 하나의 개체입니다!
또한 세로 스크롤 바로 작업하기 위한 함수를 작성합니다. 글 시작 부분에서 이미 언급했듯이 스크롤 바는 두 개의 그래픽 개체 OBJ_RECTANGLE_LABEL(사각형 레이블)를 사용하여 생성됩니다. 하나는 배경으로 사용하고 다른 하나는 스크롤 상자로 사용합니다. 스크롤바는 지표 하위 창의 오른쪽에 위치합니다.
CreateRectangleLable() - 직사각형 레이블을 만드는 함수:
//+------------------------------------------------------------------+ //| Creating a rectangle | //+------------------------------------------------------------------+ void CreateRectangleLable(long chart_id, // chart id int sub_window, // window number string object_name, // object name int x_distance, // X-coordinate int y_distance, // Y-coordinate int x_size, // width int y_size, // height ENUM_BASE_CORNER corner, // chart corner color border_color, // border color color background_color, // background color bool selectable, // cannot select the object if FALSE bool is_on_background) // background position { //--- If the object has been created successfully if(ObjectCreate(chart_id,object_name,OBJ_RECTANGLE_LABEL,sub_window,0,0)) { // set its properties ObjectSetInteger(chart_id,object_name,OBJPROP_XDISTANCE,x_distance); ObjectSetInteger(chart_id,object_name,OBJPROP_YDISTANCE,y_distance); ObjectSetInteger(chart_id,object_name,OBJPROP_XSIZE,x_size); ObjectSetInteger(chart_id,object_name,OBJPROP_YSIZE,y_size); ObjectSetInteger(chart_id,object_name,OBJPROP_BORDER_TYPE,BORDER_FLAT); // set the flat border style ObjectSetInteger(chart_id,object_name,OBJPROP_COLOR,border_color); ObjectSetInteger(chart_id,object_name,OBJPROP_CORNER,corner); ObjectSetInteger(chart_id,object_name,OBJPROP_BGCOLOR,background_color); ObjectSetInteger(chart_id,object_name,OBJPROP_SELECTABLE,selectable); ObjectSetInteger(chart_id,object_name,OBJPROP_BACK,is_on_background); // it will be used as a background if true ObjectSetString(chart_id,object_name,OBJPROP_TOOLTIP,"\n"); // no tooltip if "\n" } }
스크롤 상자와 스크롤바 배경 크기를 생성하고 수정하는 함수를 작성해 보겠습니다. AdjustScrollbarThumb() 및 AdjustScrollbarBackground():
//+------------------------------------------------------------------+ //| Adding scroll box or adjusting its size | //+------------------------------------------------------------------+ void AdjustScrollbarThumb() { //--- Calculate the scroll box size relative to the subwindow height CalculateScrollbarThumbHeight(); //--- If the scroll box is already available in the chart, adjust its properties if(ObjectFind(0,scrollbar_thumb_name)>0) { //--- Set the height and X-coordinate ObjectSetInteger(0,scrollbar_thumb_name,OBJPROP_YSIZE,scrollbar_thumb_height); ObjectSetInteger(0,scrollbar_thumb_name,OBJPROP_XDISTANCE,chart_width-scrollbar_thumb_width); //--- Adjust the scroll box position along the Y-axis if you go below the subwindow bottom boundary if(scrollbar_thumb_y1+scrollbar_thumb_height>subwindow_height) ObjectSetInteger(0,scrollbar_thumb_name,OBJPROP_YDISTANCE,subwindow_height-scrollbar_thumb_height); } //--- Create the scroll box if it does not exist else { CreateRectangleLable(0,subwindow_number,scrollbar_thumb_name, chart_width-scrollbar_thumb_width,0,scrollbar_thumb_width,scrollbar_thumb_height, CORNER_LEFT_UPPER,clrSilver,clrSilver,false,false); } } //+------------------------------------------------------------------+ //| Adding the scrollbar background or adjusting its size | //+------------------------------------------------------------------+ void AdjustScrollbarBackground() { //--- If the scrollbar background is already available in the chart, adjust its properties if(ObjectFind(0,scrollbar_background_name)>0) { //--- Set the background size ObjectSetInteger(0,scrollbar_background_name,OBJPROP_YDISTANCE,0); ObjectSetInteger(0,scrollbar_background_name,OBJPROP_XDISTANCE,chart_width-scrollbar_background_width); ObjectSetInteger(0,scrollbar_background_name,OBJPROP_YSIZE,subwindow_height); } //--- If there is no background, create it else { CreateRectangleLable(0,subwindow_number,scrollbar_background_name, chart_width-scrollbar_background_width,0,scrollbar_background_width,subwindow_height, CORNER_LEFT_UPPER,scrollbar_background_color,scrollbar_background_color,false,false); } }
스크롤 상자 높이는 강조 표시된 문자열에서 AdjustScrollbarThumb() 함수의 맨 처음에 계산됩니다.
//+------------------------------------------------------------------+ //| Calculating the scroll box size relative to the subwindow height | //+------------------------------------------------------------------+ void CalculateScrollbarThumbHeight() { //--- If the subwindow height is greater than the list size, save the subwindow size if(subwindow_height>=list_height) scrollbar_thumb_height=subwindow_height-1; //--- Otherwise calculate the scroll box size else { double height_temp=0.0; //--- Calculate the scroll box size relative to the subwindow height height_temp=subwindow_height-(((double)subwindow_height/100)*(100-((double)subwindow_height/list_height)*100)); //--- Set the minimum size at 25% of the subwindow height if(height_temp/subwindow_height<0.25) height_temp=subwindow_height/4; //--- Save to the global variable scrollbar_thumb_height=(int)height_temp; } }
그래픽 개체를 삭제하는 것을 잊지 마십시오.
//+------------------------------------------------------------------+ //| Deleting the scrollbar | //+------------------------------------------------------------------+ void DeleteScrollbar() { DeleteObjectByName(scrollbar_thumb_name); DeleteObjectByName(scrollbar_background_name); } //+------------------------------------------------------------------+ //| Deleting the object by name | //+------------------------------------------------------------------+ void DeleteObjectByName(string object_name) { //--- If such object exists if(ObjectFind(0,object_name)>=0) { //--- If an error occurred when deleting, print the relevant message if(!ObjectDelete(0,object_name)) Print("Error ("+IntegerToString(GetLastError())+") when deleting the object!"); } }
이제 가장 흥미로운 부분으로 넘어가겠습니다. 스크롤 상자를 끌어 목록도 이동하도록 하는 함수를 작성해야 합니다. 또한 커서가 스크롤 상자 위에 놓일 때와 스크롤 상자를 클릭하여 컨트롤이 스크롤 상자로 전달되었으며 이제 스크롤 상자를 끌 수 있음을 나타내기 위해 스크롤 상자의 색상 변경을 구현해야 합니다. 이를 위해 클릭 시 스크롤 상자 색상이 추가로 변경됩니다.
스크롤 상자의 너비가 상당히 좁기 때문에 위/아래로 이동할 때 커서가 옆으로 이동하는 것을 경험할 수 있습니다. 이 문제를 해결하기 위해 왼쪽 마우스 버튼을 누르고 있는 동안 스크롤 상자에 컨트롤을 전달합니다.
아래는 위에서 설명한 기능의 코드입니다.
//+------------------------------------------------------------------+ //| Setting the scroll box color | //+------------------------------------------------------------------+ void SetScrollbarThumbColor(color thumb_color) { ObjectSetInteger(0,scrollbar_thumb_name,OBJPROP_COLOR,thumb_color); ObjectSetInteger(0,scrollbar_thumb_name,OBJPROP_BGCOLOR,thumb_color); } //+------------------------------------------------------------------+ //| Setting the scroll box boundaries | //+------------------------------------------------------------------+ void SetScrollbarThumbBoundaries() { scrollbar_thumb_x1=(int)ObjectGetInteger(0,scrollbar_thumb_name,OBJPROP_XDISTANCE); scrollbar_thumb_y1=(int)ObjectGetInteger(0,scrollbar_thumb_name,OBJPROP_YDISTANCE); scrollbar_thumb_x2=scrollbar_thumb_x1+scrollbar_thumb_width; scrollbar_thumb_y2=scrollbar_thumb_y1+scrollbar_thumb_height; } //+------------------------------------------------------------------+ //| Changing the color of the scroll box when the cursor hovers over | //+------------------------------------------------------------------+ void ChangeScrollbarThumbColorOnHover(int x,int y) { //--- If the cursor is within the scroll box area, make the color darker if(x>scrollbar_thumb_x1 && x<scrollbar_thumb_x2 && y>scrollbar_thumb_y1 && y<scrollbar_thumb_x2) SetScrollbarThumbColor(scrollbar_thumb_color_on_hover); //--- If the cursor is outside the scroll box boundaries else { //--- If the mouse button is released, set the standard scroll box color if(!mouse_button_state) SetScrollbarThumbColor(scrollbar_thumb_color); } } //+------------------------------------------------------------------+ //| Determining the scroll box state | //+------------------------------------------------------------------+ void SetScrollbarThumbState(int x,int y) { //--- If the mouse cursor is within the scroll box boundaries if(x>scrollbar_thumb_x1 && x<scrollbar_thumb_x2 && y>scrollbar_thumb_y1 && y<scrollbar_thumb_x2) { //--- If the mouse button is pressed, save it if(mouse_button_state) scrollbar_thumb_clicked=true; } //--- If the cursor is outside the scroll box boundaries else { //--- If the mouse button is released, disable the scroll box control if(!mouse_button_state) ZeroScrollbarThumbVariables(); } } //+------------------------------------------------------------------+ //| Zeroing out variables related to scroll box movement | //+------------------------------------------------------------------+ void ZeroScrollbarThumbVariables() { scrollbar_thumb_clicked =false; scrollbar_fix_point =0; scrollbar_fix_point_y_offest =0; }
스크롤 상자를 이동하는 데 필요한 모든 기능은 아닙니다. 사실 스크롤 상자의 이동성은 이벤트를 기반으로 합니다. 즉, 추적 차트 영역에 커서가 있는 상태에서 마우스 버튼을 누른 다음 마우스 버튼을 계속 누르고 있는 상태에서 지정된 픽셀 수만큼 커서를 이동하면 특정 동작이 트리거됩니다. 이 경우 스크롤 상자 위치가 변경되고 결과적으로 기호 속성 목록이 변경됩니다. 그건 아주 간단합니다.
아래에서 위 작업을 구현하는 데 사용되는 MoveThumb(), UpdateListAndScrollbarThumb() 및 ThumbYCoordinateToPercent() 함수를 볼 수 있습니다.
//+------------------------------------------------------------------+ //| Moving the scroll box vertically to the specified coordinate | //+------------------------------------------------------------------+ void MoveThumb(int y) { int threshold =1; // Threshold in pixels for recalculation int new_y_point =0; // New Y-coordinate //--- If the mouse button is pressed if(mouse_button_state) { //--- Set the clicked scroll box color SetScrollbarThumbColor(scrollbar_thumb_color_on_click); //--- Save the current Y-coordinate of the cursor if(scrollbar_fix_point==0) scrollbar_fix_point=y; //--- Save the distance from the scroll box top to the cursor if(scrollbar_fix_point_y_offest==0) scrollbar_fix_point_y_offest=scrollbar_thumb_y1-scrollbar_fix_point; } //--- If you scrolled down below the threshold value, while keeping the button pressed if(y-scrollbar_fix_point>=threshold) { //--- If you are still within the subwindow boundaries if(scrollbar_thumb_y1+scrollbar_thumb_height+threshold<subwindow_height) new_y_point=y+scrollbar_fix_point_y_offest; else { scrollbar_fix_point_y_offest=0; new_y_point=int(subwindow_height-scrollbar_thumb_height)-1; } //--- Refresh the list and the scroll box UpdateListAndScrollbarThumb(new_y_point); return; } //--- If you scrolled up above the threshold value, while keeping the button pressed if(y-scrollbar_fix_point<=-(threshold)) { //--- If you are still within the subwindow boundaries if(y-fabs(scrollbar_fix_point_y_offest)>=0) new_y_point=y-fabs(scrollbar_fix_point_y_offest); else { new_y_point=0; scrollbar_fix_point_y_offest=0; } //--- Refresh the list and the scroll box UpdateListAndScrollbarThumb(new_y_point); return; } } //+------------------------------------------------------------------+ //| Refreshing the list and the scroll box position | //+------------------------------------------------------------------+ void UpdateListAndScrollbarThumb(int new_point) { //--- Set the new Y-coordinate for the scroll box ObjectSetInteger(0,scrollbar_thumb_name,OBJPROP_YDISTANCE,new_point); //--- Refresh the list of the symbol properties relative to the current scroll box position ShowSymbolInfo(ThumbYCoordinateToPercent(new_point)); //--- Zero out the fix point scrollbar_fix_point=0; } //+------------------------------------------------------------------+ //| Converting the Y-coordinate of the scroll box to percentage | //+------------------------------------------------------------------+ double ThumbYCoordinateToPercent(long y) { if(subwindow_height<=0) subwindow_height=1; //--- return(((double)y/subwindow_height)*100); }
이제 프로그램이 처음에 생각한 대로 작동할 수 있도록 모든 기능을 특정 순서로 배치해야 합니다. OnChartEvent() 함수에서 사용자가 지표 하위 창과 상호 작용하는 데 도움이 되는 이벤트와 하위 창에 있는 목록 및 스크롤 바를 처리해야 합니다.
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Track mouse clicks on the chart if(id==CHARTEVENT_CLICK) { //--- Zero out variables related to the scroll box movement ZeroScrollbarThumbVariables(); //--- Refresh the chart ChartRedraw(); return; } //--- Track the cursor movement and the state of the left mouse button if(id==CHARTEVENT_MOUSE_MOVE) { int x =(int)lparam; // X-coordinate int y =(int)dparam; // Y-coordinate int window =WRONG_VALUE; // Number of the window where the cursor is located datetime time =NULL; // Time corresponding to the X-coordinate double price =0.0; // Price corresponding to the Y-coordinate //--- Set subwindow properties SetSubwindowProperties(); //--- Check and save the state of the mouse button CheckMouseButtonState(sparam); //--- Get the position of the cursor if(ChartXYToTimePrice(0,x,y,window,time,price)) { //--- If the cursor is within the subwindow boundaries if(window==subwindow_number) { //--- Disable scrolling of the price chart ChartSetInteger(0,CHART_MOUSE_SCROLL,false); //--- Recalculate the Y coordinate relative to the indicator subwindow ChartYToSubwindowY(y); //--- Determine the scroll box boundaries SetScrollbarThumbBoundaries(); //--- Change the scroll box color when the cursor moves over it ChangeScrollbarThumbColorOnHover(x,y); //--- Determine the scroll box state SetScrollbarThumbState(x,y); //--- If the control is passed to the scroll box, drag it and refresh the list if(scrollbar_thumb_clicked) MoveThumb(y); } //--- If the cursor is outside the subwindow boundaries else { //--- Enable scrolling of the price chart ChartSetInteger(0,CHART_MOUSE_SCROLL,true); //--- If the mouse button is released, set the standard scroll box color if(!scrollbar_thumb_clicked) SetScrollbarThumbColor(scrollbar_thumb_color); } } //--- If the position of the cursor could not be determined else { //--- If the control has not been passed to the scroll box, set the standard scroll box color if(!scrollbar_thumb_clicked) SetScrollbarThumbColor(scrollbar_thumb_color); } //--- Refresh the chart ChartRedraw(); return; } //--- Track the change of properties and size of the chart if(id==CHARTEVENT_CHART_CHANGE) { //--- Set subwindow properties SetSubwindowProperties(); //--- Get the Y-coordinate of the scroll box scrollbar_thumb_y1=(int)ObjectGetInteger(0,scrollbar_thumb_name,OBJPROP_YDISTANCE); //--- If the subwindow size is zero, exit if(subwindow_height<=0) return; //--- Set the new canvas size ResizeCanvas(); //--- Refresh the scrollbar background AdjustScrollbarBackground(); //--- Refresh the scroll box AdjustScrollbarThumb(); //--- Refresh the data on the canvas ShowSymbolInfo(ThumbYCoordinateToPercent(scrollbar_thumb_y1)); //--- return; } }
위 코드에서 강조 표시된 기능은 보조 기능입니다. 제공된 주석에서 목적을 쉽게 이해할 수 있습니다.
//+-------------------------------------------------------------------+ //| Checking the mouse button state | //+-------------------------------------------------------------------+ void CheckMouseButtonState(string state) { //--- Left mouse button is pressed if(state=="1") mouse_button_state=true; //--- Left mouse button is released if(state=="0") { //--- Zero out variables ZeroScrollbarThumbVariables(); mouse_button_state=false; } } //+-------------------------------------------------------------------+ //| Recalculating the Y-coordinate relative to the indicator subwindow| //+-------------------------------------------------------------------+ void ChartYToSubwindowY(int &y) { //--- Get the distance from the chart top to the indicator subwindow chart_y_offset=(int)ChartGetInteger(0,CHART_WINDOW_YDISTANCE,subwindow_number); //--- Recalculate the Y-coordinate relative to the indicator subwindow y-=chart_y_offset; }
캔버스와 마찬가지로 초기화 중에 지표 하위 창에 스크롤 바를 추가해야 합니다.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Enable tracking of mouse events ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true); //--- Set the short name IndicatorSetString(INDICATOR_SHORTNAME,subwindow_shortname); //--- Set sizes of arrays of symbol properties and their colors ArrayResize(symbol_property_colors,LIST_SIZE); ArrayResize(symbol_property_values,LIST_SIZE); //--- Set subwindow properties SetSubwindowProperties(); //--- Set the font for displaying on the canvas canvas.FontSet(font_name,font_size,FW_NORMAL); //--- Save the text size (height) for calculations text_height=canvas.TextHeight("A")-1; //--- Calculate the height of the entire list list_height=text_height*LIST_SIZE; //--- Add the canvas to the chart AddCanvas(); //--- Add the scrollbar: background and scroll box AdjustScrollbarBackground(); AdjustScrollbarThumb(); //--- Display the list of symbol properties ShowSymbolInfo(); //--- Refresh the chart ChartRedraw(); //--- Everything completed successfully return(INIT_SUCCEEDED); }
OnDeinit() 함수에서 "정리"하는 것을 잊지 마십시오. 초기화 해제 사유에 따라 프로그램을 보다 정확하게 설정할 수 있습니다.
//+------------------------------------------------------------------+ //| Deinitialization | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(reason==REASON_REMOVE || // If the indicator has been removed from the chart or reason==REASON_CHARTCHANGE || // the symbol or time frame has been modified or reason==REASON_RECOMPILE || // the program has been recompiled or reason==REASON_CHARTCLOSE || // the chart has been closed or reason==REASON_CLOSE) // the terminal has been closed { //--- Delete the scrollbar DeleteScrollbar(); //--- Delete the canvas DeleteCanvas(); //--- Enable scrolling of the price chart ChartSetInteger(0,CHART_MOUSE_SCROLL,true); //--- Disable cursor tracking ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,false); //--- Refresh the chart ChartRedraw(); } }
마지막으로 실시간 모드에서 일부 기호 속성을 새로 고치려면 OnCalculate() 함수에 몇 가지 코드 문자열을 추가해야 합니다.
이제 모든 것이 준비되었습니다. 소스 코드는 MetaEditor 5에서 고려할 수 있도록 글에 첨부되어 있으며 다운로드할 수 있습니다. 이 글에서 다루는 기능의 작동은 아래 제공된 비디오에 나와 있습니다.
결론
스크롤바 컨트롤에 대한 검토를 완료했습니다. 이 글에서는 캔버스에 있는 별도의 그래픽 개체로 스크롤바를 만드는 방법을 보여주었습니다. 향후 글 중 하나에서 이 클래스의 를 사용하여 전체 기능을 구현하려고 합니다.
MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/751



