#property copyright "Стандартная оценка. StdScore.mq4 4.0. Copyright © FXcoder, 2009-2014"
#property link      "http://fxcoder.net"
#property strict

#property indicator_separate_window
#property indicator_buffers 11            // The first buffer is for the value, other 10 are for candle drawing
#property indicator_color1  clrRoyalBlue  // Value
#property indicator_color2  clrLimeGreen  // BullBody
#property indicator_color3  clrLimeGreen  // BullBody2
#property indicator_color4  clrRed        // BearBody
#property indicator_color5  clrRed        // BearBody2
#property indicator_color6  clrNONE       // BodyMask
#property indicator_color7  clrLimeGreen  // BullShadow
#property indicator_color8  clrLimeGreen  // BullShadow2
#property indicator_color9  clrRed        // BearShadow
#property indicator_color10 clrRed        // BearShadow2
#property indicator_color11 clrNONE       // ShadowMask

#property indicator_width1 1

#property indicator_levelcolor Gray
#property indicator_levelstyle STYLE_DOT

#property indicator_level1 2
#property indicator_level2 0
#property indicator_level3 -2

#property indicator_minimum -4
#property indicator_maximum 4

enum ENUM_CHART_TYPE {
	CHART_TYPE_LINE = 0,                            // Line
	CHART_TYPE_BAR = 1,                             // Bar
	CHART_TYPE_CANDLESTICK = 2                      // Candlestick
};

// Период расчета
input int N = 20;                                          // Period

// Инструмент
input string Sym = "";                                     // Symbol

// Реверс
input bool Reverse = false;                                // Reverse

// Тип цены для расчета
input ENUM_APPLIED_PRICE AppliedPrice = PRICE_MEDIAN;      // Applied price

// Тип цены значения
input ENUM_APPLIED_PRICE ValuePrice = PRICE_CLOSE;         // Value and line price

// Метод отображения: 0 - линия, 1 - бары, 2 - свечи
input ENUM_CHART_TYPE ChartType = CHART_TYPE_CANDLESTICK;  // Chart type

// Цвет бычьей свечи
input color BullColor = clrRoyalBlue;                      // Bull color

// Цвет медвежьей свечи
input color BearColor = clrCrimson;                        // Bear color

// Цвет фона, CLR_NONE - автоопределение
input color BackColor = clrNONE;                           // Background color (None=auto)

// Толщина тела свечи
input int BodyWidth = 0;                                   // Body width (0=auto)

// Ограничение количества баров для расчета и отбражения
input int MaxBars = 0;                                     // Maximum bars

// Сдвиг вправо
input int Shift = 0;                                       // Shift
input int Smooth = 0;                                      // super smoother period

double Value[], BullBody[], BullBody2[], BearBody[], BearBody2[], BodyMask[], BullShadow[], BullShadow2[], BearShadow[], BearShadow2[], ShadowMask[];

datetime _lastTickTime = 0;
int _bodyWidth = 2;
color _bgColor;
string _sym;
int _n;

int start()
{
	updateBodyWidth();
	updateMaskColor();

	int countedBars = IndicatorCounted();

	if (countedBars > 0)
		countedBars--;

	int barsCount = Bars - countedBars;

	if (MaxBars > 0)
	{
		if (Bars - countedBars > 2)
		{
			barsCount = MaxBars;
			clearData();
		}
		else
		{
			clearData(MaxBars);
			barsCount = MathMin(barsCount, MaxBars);
		}
	}

	// в мультивалютном пересчитываем несколько последних баров, а не один
	if (_lastTickTime != 0 && _sym != Symbol())
		barsCount = MathMax(barsCount, iBarShift(Symbol(), Period(), _lastTickTime, false) + 1);

	double o, h, l, c, value, ma, sd;

	for (int i = barsCount - 1; i >= 0; i--)
	{

		clearData(i);

		o = EMPTY_VALUE;
		h = EMPTY_VALUE;
		l = EMPTY_VALUE;
		c = EMPTY_VALUE;
		value = EMPTY_VALUE;
		ma = EMPTY_VALUE;
		sd = 0;
		int si = i;

		if (_sym == Symbol())
		{
			o = Open[i];
			h = High[i];
			l = Low[i];
			c = Close[i];
		}
		else
		{
			// определить бар для другого символа
			datetime dt = iTime(Symbol(), Period(), i);
			si = iBarShift(_sym, Period(), dt, true);

			if (si != -1)
			{
				// бар тот же
				o = iOpen(_sym, Period(), si);
				h = iHigh(_sym, Period(), si);
				l = iLow(_sym, Period(), si);
				c = iClose(_sym, Period(), si);

				_lastTickTime = dt; // запоминаем время последнего тика
			}
			else
			{
				// в случае отсутствия данных по символу брать прошлый бар и его цену закрытия
				si = iBarShift(_sym, Period(), dt, false);

				if (si != -1)
				{
					// защита от подглядывания в будущее
					datetime sdt = iTime(_sym, Period(), si);

					if (sdt > dt)
						si++;

					o = iClose(_sym, Period(), si);
					h = o;
					l = o;
					c = o;
				}
			}
		}

		if (c != EMPTY_VALUE)
		{
			// Вычитание средней
			ma = iMA(_sym, Period(), _n, 0, MODE_SMA, AppliedPrice, si);

			if (ma > 0)
			{
				o = o - ma;
				h = h - ma;
				l = l - ma;
				c = c - ma;

				value = getPrice(ValuePrice, o, h, l, c);

				// Деление на стандартное отклонение

				sd = iStdDev(_sym, Period(), _n, 0, MODE_SMA, AppliedPrice, si);

				if (sd > 0)
				{
					o = o / sd;
					h = h / sd;
					l = l / sd;
					c = c / sd;

					value = getPrice(ValuePrice, o, h, l, c);

					if (Reverse)
					{
						o = -o;
						h = -h;
						l = -l;
						c = -c;

						// меняем местами верх и низ для корректной отрисовки
						swap(h, l);

						value = getPrice(ValuePrice, o, h, l, c);
					}
				} // sd > 0
			} // ma > 0
		} // c != EMPTY_VALUE

		Value[i] = iSsm(value,Smooth,i);

		if (ChartType != CHART_TYPE_LINE)
			drawBar(o, h, l, c, i);
	} // for: bars loop

	return(0);
}

int init()
{
	_n = N >= 2 ? N : 2;
	_sym = Sym == "" ? Symbol() : UpperString(Sym);

	_bodyWidth = BodyWidth;

	if (ChartType == CHART_TYPE_LINE)
	{
		SetIndexStyle(0, DRAW_LINE, EMPTY, EMPTY, BullColor);
		SetIndexStyle(1, DRAW_NONE);
		SetIndexStyle(2, DRAW_NONE);
		SetIndexStyle(3, DRAW_NONE);
		SetIndexStyle(4, DRAW_NONE);
		SetIndexStyle(5, DRAW_NONE);
		SetIndexStyle(6, DRAW_NONE);
		SetIndexStyle(7, DRAW_NONE);
		SetIndexStyle(8, DRAW_NONE);
		SetIndexStyle(9, DRAW_NONE);
		SetIndexStyle(10, DRAW_NONE);
	}
	else
	{
		SetIndexStyle(0, DRAW_NONE, EMPTY, EMPTY, clrNONE);
		SetIndexStyle(1, DRAW_HISTOGRAM, EMPTY, _bodyWidth, BullColor);
		SetIndexStyle(2, DRAW_HISTOGRAM, EMPTY, _bodyWidth, BullColor);
		SetIndexStyle(3, DRAW_HISTOGRAM, EMPTY, _bodyWidth, BearColor);
		SetIndexStyle(4, DRAW_HISTOGRAM, EMPTY, _bodyWidth, BearColor);
		SetIndexStyle(5, DRAW_HISTOGRAM, EMPTY, _bodyWidth, _bgColor);
		SetIndexStyle(6, DRAW_HISTOGRAM, EMPTY, 1, BullColor);
		SetIndexStyle(7, DRAW_HISTOGRAM, EMPTY, 1, BullColor);
		SetIndexStyle(8, DRAW_HISTOGRAM, EMPTY, 1, BearColor);
		SetIndexStyle(9, DRAW_HISTOGRAM, EMPTY, 1, BearColor);
		SetIndexStyle(10, DRAW_HISTOGRAM, EMPTY, 1, _bgColor);
	}

	SetIndexBuffer(0, Value);
	SetIndexBuffer(1, BullBody);
	SetIndexBuffer(2, BullBody2);
	SetIndexBuffer(3, BearBody);
	SetIndexBuffer(4, BearBody2);
	SetIndexBuffer(5, BodyMask);
	SetIndexBuffer(6, BullShadow);
	SetIndexBuffer(7, BullShadow2);
	SetIndexBuffer(8, BearShadow);
	SetIndexBuffer(9, BearShadow2);
	SetIndexBuffer(10, ShadowMask);

	for (int i = 0; i <= 10; i++)
		SetIndexShift(i, Shift);

	SetIndexLabel(0, "Value");
	SetIndexLabel(1, "Bull Body");
	SetIndexLabel(2, "Bull Body 2");
	SetIndexLabel(3, "Bear Body");
	SetIndexLabel(4, "Bear Body 2");
	SetIndexLabel(5, "Body Mask");
	SetIndexLabel(6, "Bull Shadow");
	SetIndexLabel(7, "Bull Shadow 2");
	SetIndexLabel(8, "Bear Shadow");
	SetIndexLabel(9, "Bear Shadow 2");
	SetIndexLabel(10, "Shadow Mask");

	string name =
		"StdScore(" +
		(Reverse || _sym != Symbol()
			? (Reverse ? "1/" : "") + _sym + ","
			: "")
		 + IntegerToString(_n) + ")";

	IndicatorShortName(name);

	return(0);
}

void updateMaskColor()
{
	// Определить цвета фона
	color bgColor = ColorIsNone(BackColor) ? GetBGColor() : BackColor;

	// Если цвет фона определен, переопределить цвет масок
	if (ColorIsNone(bgColor))
		return;

	SetIndexStyle(5, DRAW_HISTOGRAM, EMPTY, EMPTY, bgColor);
	SetIndexStyle(10, DRAW_HISTOGRAM, EMPTY, EMPTY, bgColor);
}

void updateBodyWidth()
{
	if (BodyWidth > 0)
		return;

	// Определить толщину тела бара
	int newBodyWidth = barStepToBodyWidth(GetBarStep());

	if (newBodyWidth == _bodyWidth)
		return;

	_bodyWidth = newBodyWidth;

	if (ChartType == CHART_TYPE_BAR)
	{
		for (int i = 6; i <= 10; i++)
			SetIndexStyle(i, DRAW_HISTOGRAM, EMPTY, _bodyWidth);
	}
	else
	{
		for (int i = 1; i <= 5; i++)
			SetIndexStyle(i, DRAW_HISTOGRAM, EMPTY, _bodyWidth);
	}
}

void swap(double& a, double& b)
{
	double c = b;
	b = a;
	a = c;
}

void clearData(int bar = -1)
{
	if (bar == -1)
	{
		ArrayInitialize(Value, EMPTY_VALUE);
		ArrayInitialize(BullBody, EMPTY_VALUE);
		ArrayInitialize(BullBody2, EMPTY_VALUE);
		ArrayInitialize(BearBody, EMPTY_VALUE);
		ArrayInitialize(BearBody2, EMPTY_VALUE);
		ArrayInitialize(BodyMask, EMPTY_VALUE);
		ArrayInitialize(BullShadow, EMPTY_VALUE);
		ArrayInitialize(BullShadow2, EMPTY_VALUE);
		ArrayInitialize(BearShadow, EMPTY_VALUE);
		ArrayInitialize(BearShadow2, EMPTY_VALUE);
		ArrayInitialize(ShadowMask, EMPTY_VALUE);
	}
	else
	{
		Value[bar] = EMPTY_VALUE;
		BullBody[bar] = EMPTY_VALUE;
		BullBody2[bar] = EMPTY_VALUE;
		BearBody[bar] = EMPTY_VALUE;
		BearBody2[bar] = EMPTY_VALUE;
		BodyMask[bar] = EMPTY_VALUE;
		BullShadow[bar] = EMPTY_VALUE;
		BullShadow2[bar] = EMPTY_VALUE;
		BearShadow[bar] = EMPTY_VALUE;
		BearShadow2[bar] = EMPTY_VALUE;
		ShadowMask[bar] = EMPTY_VALUE;
	}
}

double getPrice(int ap, double o, double h, double l, double c)
{
	switch(ap)
	{
		case PRICE_CLOSE: return(c);
		case PRICE_OPEN: return(o);
		case PRICE_HIGH: return(h);
		case PRICE_LOW: return(l);
		case PRICE_MEDIAN: return((h + l) / 2.0);
		case PRICE_TYPICAL: return((h + l + c) / 3.0);
		case PRICE_WEIGHTED: return((h + l + 2.0 * c) / 4.0);
	}

	return(0);
}

// Нарисовать бар
void drawBar(double open, double high, double low, double close, int bar)
{
	bool isBull = close >= open;
	bool isBear = !isBull;

	double bodyMax = open > close ? open : close;
	double bodyMin = open < close ? open : close;

	if (ChartType == CHART_TYPE_CANDLESTICK)
	{
		BullBody[bar] = isBull && (close >= 0) ? close : EMPTY_VALUE;
		BullBody2[bar] = isBull && (open < 0) ? open : EMPTY_VALUE;

		BearBody[bar] = isBear && (open >= 0) ? open : EMPTY_VALUE;
		BearBody2[bar] = isBear && (close < 0) ? close : EMPTY_VALUE;

		BodyMask[bar] = (bodyMin > 0) ? bodyMin : (bodyMax < 0 ? bodyMax : EMPTY_VALUE);
	}

	BullShadow[bar] = isBull && (high >= 0) ? high : EMPTY_VALUE;
	BullShadow2[bar] = isBull && (low < 0) ? low : EMPTY_VALUE;

	BearShadow[bar] = isBear && (high >= 0) ? high : EMPTY_VALUE;
	BearShadow2[bar] = isBear && (low < 0) ? low : EMPTY_VALUE;

	ShadowMask[bar] = low >= 0 ? low : (high < 0 ? high : EMPTY_VALUE);
}

#import "gdi32.dll"
uint GetPixel(int hDC, int x, int y);
#import

#import "user32.dll"
int GetWindowDC(int h);
int ReleaseDC(int h, int hDC);
bool GetWindowRect(int h, int& pos[4]);
#import

color GetBGColor(int h = 0)
{
	color bgColor = CLR_NONE;

	if (h == 0)
		h = WindowHandle(Symbol(), Period());

	// Получить цвет фона заданного окна
	if (h != 0)
	{
		int hDC = GetWindowDC(h);

		// Отрезок из двух точек, пересекающий рамку чарта
		color c1 = (color)GetPixel(hDC, 3, 10); // фон
		color c2 = (color)GetPixel(hDC, 4, 10); // рамка

		// Если рамка есть, то цвет определен верно (окно активно)
		if (c1 != c2)
			bgColor = c1;

		ReleaseDC(h, hDC);
	}

	return(bgColor);
}

bool ColorIsNone(color c)
{
	return((c >> 24) > 0);
}

string UpperString(string s)
{
    StringToUpper(s);
	return(s);
}

bool GetWindowBounds(int &left, int &top, int &right, int &bottom)
{
	int handle = WindowHandle(Symbol(), Period());

	if (handle == 0)
		return(false);

	int rect[4];

	if (GetWindowRect(handle, rect))
	{
		left = rect[0];
		top = rect[1];
		// "системный" прямоугольник содержит избыточные ряд и столбец, здесь это не нужно, т.к. приведет к путанице
		right = rect[2] - 1;
		bottom = rect[3] - 1;

		return(true);
	}
	else
	{
		return(false);
	}
}

int GetBarStep()
{
	int left, top, right, bottom;
	int bw = 1; // ширина по умолчанию (не определено)

	if (GetWindowBounds(left, top, right, bottom))
	{
		int width = right - left - 5 - 42;

		if (width != 0 && WindowBarsPerChart()!=0)
		{
			bw = (int)MathRound(1.0 * width / WindowBarsPerChart());

			// коррекция (все ширины бара - степени двойки)
			bw = (int)MathPow(2, MathRound(MathLog(bw) / MathLog(2)));
		}
	}

	return(bw);
}

int barStepToBodyWidth(int step)
{
	if (step < 4)
		return(1);

	if (step < 8)
		return(2);

	if (step < 16)
		return(3);

	if (step < 32)
		return(6);

	// >=32
	return(12);
}

// 2014-09-17 21:10:11 UTC
// MQLMake 1.22. Copyright © FXcoder, 2011-2014. http://fxcoder.net

//
//
//
//
//

#define Pi 3.14159265358979323846264338327950288
double workSsm[][2];
#define _tprice 0
#define _ssm    1

double workSsmCoeffs[][4];
#define _period 0
#define _c1     1
#define _c2     2
#define _c3     3

//
//
//
//
//

double iSsm(double price, double period, int i, int instanceNo=0)
{
   if (period<=1) return(price); i = Bars-i-1;
   if (ArrayRange(workSsm,0) !=Bars)                 ArrayResize(workSsm,Bars);
   if (ArrayRange(workSsmCoeffs,0) < (instanceNo+1)) ArrayResize(workSsmCoeffs,instanceNo+1);
   if (workSsmCoeffs[instanceNo][_period] != period)
   {
      workSsmCoeffs[instanceNo][_period] = period;
      double a1 = MathExp(-1.414*Pi/period);
      double b1 = 2.0*a1*MathCos(1.414*Pi/period);
         workSsmCoeffs[instanceNo][_c2] = b1;
         workSsmCoeffs[instanceNo][_c3] = -a1*a1;
         workSsmCoeffs[instanceNo][_c1] = 1.0 - workSsmCoeffs[instanceNo][_c2] - workSsmCoeffs[instanceNo][_c3];
   }

   //
   //
   //
   //
   //

      int s = instanceNo*2;   
          workSsm[i][s+_tprice] = price; 
          if ((i-2)>=0)
          workSsm[i][s+_ssm]    = workSsmCoeffs[instanceNo][_c1]*(workSsm[i][s+_tprice]+workSsm[i-1][s+_tprice])/2.0 + 
                                  workSsmCoeffs[instanceNo][_c2]*workSsm[i-1][s+_ssm]                                + 
                                  workSsmCoeffs[instanceNo][_c3]*workSsm[i-2][s+_ssm]; 
   return(workSsm[i][s+_ssm]);
}