generated from LengKundee/UA6-9V_VL6-N9
294 lines
11 KiB
MQL5
294 lines
11 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| ShowWeeks.mq5 |
|
|
//| Copyright (c) 2009-2024, Marketeer |
|
|
//| https://www.mql5.com/en/users/marketeer |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright (c) 2009-2024, Marketeer"
|
|
#property link "https://www.mql5.com/en/users/marketeer"
|
|
#property description "Tracks current mouse position to show day of week, week number, day of year, or bar index under the cursor."
|
|
#property description "Two selected parameters are combined in a single value as a whole part and a fractional part in the Data Window."
|
|
#property version "1.0"
|
|
|
|
#property indicator_chart_window 0
|
|
#property indicator_buffers 1
|
|
#property indicator_plots 1
|
|
#property indicator_type1 DRAW_NONE
|
|
|
|
#include <MQL5Book/DateTime.mqh>
|
|
#include <MQL5Book/ArrayUtils.mqh>
|
|
|
|
#define PUSH(A,V) (A[ArrayResize(A, ArrayRange(A, 0) + 1, ArrayRange(A, 0) * 2) - 1] = V)
|
|
#define OBJ_PREFIX "WDi_"
|
|
#define DAY (60 * 60 * 24)
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| I N P U T S |
|
|
//+------------------------------------------------------------------+
|
|
|
|
enum InfoType
|
|
{
|
|
None = 1,
|
|
DoW = 10, // Day Of Week
|
|
Week = 100, // Week Of Year
|
|
DoY = 1000, // Day Of Year
|
|
Bar = 1000000000, // Bar Index
|
|
};
|
|
|
|
input group "Display In Data Window via Buffer"
|
|
input InfoType WholePart = Week;
|
|
input InfoType FractionalPart = DoY;
|
|
|
|
enum Alignment
|
|
{
|
|
Top,
|
|
Middle,
|
|
Bottom,
|
|
};
|
|
|
|
input group "Display Labels on Chart"
|
|
input bool ShowLabels = false;
|
|
input string FontName = "Segoe UI";
|
|
input int FontSize = 25;
|
|
input color FontColor = clrNONE;
|
|
input int Padding = 5; // Padding (% of chart height, for top/bottom alignment)
|
|
input Alignment AlignTo = Top;
|
|
input int RotationAngle = 0; // RotationAngle (for middle alignment)
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| G L O B A L S |
|
|
//+------------------------------------------------------------------+
|
|
|
|
double Buffer1[];
|
|
int objCache[];
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
if(_Period > PERIOD_D1) return INIT_PARAMETERS_INCORRECT;
|
|
|
|
SetIndexBuffer(0, Buffer1, INDICATOR_DATA);
|
|
IndicatorSetInteger(INDICATOR_DIGITS, (int)MathLog10(FractionalPart));
|
|
ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); // does not work in tester
|
|
|
|
if(WholePart == FractionalPart && WholePart != None)
|
|
Alert("The same info is selected to display as whole part and fractional part of indicator values!");
|
|
|
|
if(ShowLabels && _Period < PERIOD_D1) AdjustLabels();
|
|
|
|
return INIT_SUCCEEDED;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int)
|
|
{
|
|
ObjectsDeleteAll(0, OBJ_PREFIX);
|
|
ChartRedraw();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator iteration function |
|
|
//+------------------------------------------------------------------+
|
|
int OnCalculate(const int rates_total, const int prev_calculated,
|
|
const datetime &time[],
|
|
const double &open[],
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &close[],
|
|
const long &tick_volume[],
|
|
const long &volume[],
|
|
const int &spread[])
|
|
{
|
|
int limit = 0;
|
|
if(prev_calculated <= 0)
|
|
ArrayInitialize(Buffer1, EMPTY_VALUE);
|
|
else
|
|
limit = prev_calculated - 1;
|
|
|
|
for(int i = limit; i < rates_total && !IsStopped(); i++)
|
|
{
|
|
if(WholePart * FractionalPart > 1)
|
|
{
|
|
const int d = TimeDayOfYear(time[i]); // cache datetime object
|
|
Buffer1[i] = GetPart(WholePart, i) + GetPart(FractionalPart, i) / (double)FractionalPart;
|
|
}
|
|
else
|
|
{
|
|
Buffer1[i] = DayOfWeek(time[i]);
|
|
}
|
|
}
|
|
|
|
return rates_total;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Event handler function |
|
|
//+------------------------------------------------------------------+
|
|
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
|
|
{
|
|
if(id == CHARTEVENT_MOUSE_MOVE)
|
|
{
|
|
int window;
|
|
datetime time;
|
|
double price;
|
|
if(ChartXYToTimePrice(0, (int)lparam, (int)dparam, window, time, price))
|
|
{
|
|
const int b = iBarShift(_Symbol, _Period, time);
|
|
if(b > -1)
|
|
time = iTime(_Symbol, _Period, b);
|
|
else
|
|
time = (time + PeriodSeconds() / 2) / PeriodSeconds() * PeriodSeconds();
|
|
static int day = -1;
|
|
int now = DayOfWeek(time);
|
|
// Comment((string)time);
|
|
if(now != day)
|
|
{
|
|
day = now;
|
|
const bool valid = WholePart * FractionalPart > 1;
|
|
const string legend = StringFormat("%s%s%s%s", valid ? " | " : "", WholePart > 1 ? EnumToString(WholePart) : "",
|
|
FractionalPart > 1 ? "." + EnumToString(FractionalPart) : "",
|
|
valid ? ": ->" : "");
|
|
PlotIndexSetString(0, PLOT_LABEL,
|
|
StringFormat("Day: %s%s", DayName(day), legend));
|
|
ChartRedraw(0);
|
|
}
|
|
}
|
|
}
|
|
else if(id == CHARTEVENT_CHART_CHANGE)
|
|
{
|
|
if(ShowLabels && _Period < PERIOD_D1) AdjustLabels();
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Main batch processing of labels |
|
|
//+------------------------------------------------------------------+
|
|
void AdjustLabels()
|
|
{
|
|
const int left = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
|
|
const int width = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
|
|
datetime overnights[][2]; // [prev day end] [next day start]
|
|
int prevday = -1;
|
|
|
|
for(int i = 0; i < width; i++)
|
|
{
|
|
if(left - i < 0) continue;
|
|
const int day = DayOfWeek(iTime(_Symbol, _Period, left - i));
|
|
if(day != prevday || left - i == 0)
|
|
{
|
|
datetime range[1][2] =
|
|
{{
|
|
(left - i > 0) ? iTime(_Symbol, _Period, left - i + 1) : iTime(_Symbol, _Period, 0) / DAY * DAY - PeriodSeconds(),
|
|
(left - i > 0) ? iTime(_Symbol, _Period, left - i) : iTime(_Symbol, _Period, 0) / DAY * DAY + DAY
|
|
}};
|
|
ArrayInsert(overnights, range, ArrayRange(overnights, 0));
|
|
prevday = day;
|
|
}
|
|
}
|
|
|
|
const double add = Padding / 100.0 * (ChartGetDouble(0, CHART_PRICE_MAX) - ChartGetDouble(0, CHART_PRICE_MIN));
|
|
const double price =
|
|
AlignTo == Top ?
|
|
ChartGetDouble(0, CHART_PRICE_MAX) - add : (AlignTo == Bottom ? ChartGetDouble(0, CHART_PRICE_MIN) + add :
|
|
(ChartGetDouble(0, CHART_PRICE_MAX) + ChartGetDouble(0, CHART_PRICE_MIN)) / 2);
|
|
|
|
const int realFontSize = (int)(FontSize / (6 - ChartGetInteger(0, CHART_SCALE)));
|
|
|
|
for(int i = 1; i < ArrayRange(overnights, 0); i++)
|
|
{
|
|
CreateLabel(overnights[i - 1][1], overnights[i][1] > TimeCurrent() ? overnights[i][1] : overnights[i][0] + PeriodSeconds(), price, realFontSize);
|
|
}
|
|
|
|
CleanUpLabels(iTime(_Symbol, _Period, left), iTime(_Symbol, _Period, left - width), price, realFontSize);
|
|
ChartRedraw();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Create or adjust a single label |
|
|
//+------------------------------------------------------------------+
|
|
void CreateLabel(const datetime left, const datetime right, const double price, const int realFontSize)
|
|
{
|
|
if(right - left < DAY / 2) return;
|
|
|
|
iClose(_Symbol, _Period, 0);
|
|
const int d = TimeDayOfYear(left);
|
|
const string name = OBJ_PREFIX + (string)d;
|
|
ObjectCreate(0, name, OBJ_TEXT, 0, (left + right) / 2, price);
|
|
ObjectSetInteger(0, name, OBJPROP_ANCHOR, AlignTo == Top ? ANCHOR_UPPER : (AlignTo == Bottom ? ANCHOR_LOWER : ANCHOR_CENTER));
|
|
ObjectSetString(0, name, OBJPROP_TEXT, DayName(DayOfWeek(left)) +
|
|
(WholePart == Week || FractionalPart == Week ? "|" + (string)WeekNumber() : "") +
|
|
(WholePart == DoY || FractionalPart == DoY ? "|" + (string)d : ""));
|
|
ObjectSetString(0, name, OBJPROP_FONT, FontName);
|
|
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, realFontSize);
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, FontColor != clrNONE ? FontColor : ~ChartGetInteger(0, CHART_COLOR_BACKGROUND));
|
|
ObjectSetInteger(0, name, OBJPROP_BACK, true);
|
|
if(AlignTo == Middle) ObjectSetDouble(0, name, OBJPROP_ANGLE, RotationAngle);
|
|
|
|
PUSH(objCache, d);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Remove objects outside of visible part of the chart |
|
|
//+------------------------------------------------------------------+
|
|
void CleanUpLabels(const datetime left, const datetime right, const double price, const int realFontSize)
|
|
{
|
|
for(int i = 0; i < ArraySize(objCache); i++)
|
|
{
|
|
const string name = OBJ_PREFIX + (string)objCache[i];
|
|
const datetime t = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0);
|
|
if(t != 0 && (t < left || t > (right ? right : TimeCurrent() + DAY * 7)))
|
|
{
|
|
ObjectDelete(0, name);
|
|
objCache[i] = -1;
|
|
}
|
|
else
|
|
{
|
|
ObjectSetDouble(0, name, OBJPROP_PRICE, price);
|
|
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, realFontSize);
|
|
}
|
|
}
|
|
ArrayPurge(objCache, -1);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Helper functions |
|
|
//+------------------------------------------------------------------+
|
|
|
|
double GetPart(const InfoType part, const int i)
|
|
{
|
|
switch(part)
|
|
{
|
|
case DoW:
|
|
return _TimeDayOfWeek();
|
|
case DoY:
|
|
return _TimeDayOfYear();
|
|
case Week:
|
|
return WeekNumber();
|
|
case Bar:
|
|
return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
string DayName(const int DayNo)
|
|
{
|
|
const static string Days[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
|
return Days[DayNo % 7];
|
|
}
|
|
|
|
int DayOfWeek(const datetime time)
|
|
{
|
|
return (int)((time / 86400) + 4) % 7;
|
|
}
|
|
|
|
int WeekNumber(/*const datetime dt*/) // prerequisite: parameter is cached in _macros
|
|
{
|
|
const datetime d1 = StringToTime(((string)_TimeYear(/*dt*/) + ".01.01 00:00"));
|
|
|
|
return (_TimeDayOfYear() + DayOfWeek(d1)) / 7 + 1;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|