//+------------------------------------------------------------------+
//|                                                  SwingVolume.mq4 |
//|                                      Copyright © 2010, KNYI,Jong |
//|                                     http://knyijong.blogspot.com |
//+------------------------------------------------------------------+
#include <stdlib.mqh>

#property copyright "Copyright © 2010, KNYI,Jong"
#property link      "http://knyijong.blogspot.com"

#property indicator_chart_window

#property indicator_buffers 1
#property indicator_color1 Orange

#define MOMENTUM_UP		1
#define MOMENTUM_NONE	0
#define MOMENTUM_DOWN	-1

extern double turningWidth	= 24.0;	// percentage
double basisWidth;
double winMaxPrice;
double winMinPrice;

int basePoint	= 0;
int peakPoint	= 0;
int momentum 	= MOMENTUM_NONE;

double ZigzagBuffer[];

string	textPeakVolumePrefix	= "peakVolume";
int		textPeakVolumeLength	= 10;
int		textPeakVolumeCount		= 0;

string	textAverageVolumePrefix	= "averageVolume";
int		textAverageVolumeLength	= 13;
int		textAverageVolumeCount	= 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init() {
Print("start init");
	IndicatorBuffers(1);

	SetIndexStyle(0, DRAW_SECTION);
	SetIndexBuffer(0, ZigzagBuffer);
	SetIndexEmptyValue(0, 0.0);

	winMaxPrice	= WindowPriceMax();
	winMinPrice	= WindowPriceMin();
	basisWidth	= winMaxPrice - winMinPrice;
	momentum 	= MOMENTUM_NONE;
	basePoint	= 0;

Print("end init");
	return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit() {
/*
	for (int i=textPeakVolumeCount; i>0; i--) {
		ObjectDelete(textPeakVolumePrefix+i);
	}
	for (i=textAverageVolumeCount; i>0; i--) {
		ObjectDelete(textAverageVolumePrefix+i);
	}
*/
Print("start deinit");
	delFixedVolumes();	
	delZanteiVolume();

Print("end deinit");
	return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start() {
Print("start start");
	int	changeBarNum = Bars - IndicatorCounted();
	int shiftWidth;

	double currentHighValue, baseHighValue, peakHighValue;
	double currentLowValue, baseLowValue, peakLowValue; 
	double curMomentumLength,newMomentumLength;

	if (reset()) {
		// did reset
		// re calc all bars
		changeBarNum = Bars;
	}

	if (changeBarNum > 1 && momentum != MOMENTUM_NONE) {
		// exist points shift when bars added after drow chart
		shiftWidth = changeBarNum-1;
		if (basePoint != 0) {
			basePoint += shiftWidth;
		}
		if (peakPoint != 0) {
			peakPoint += shiftWidth;
		}
	}

	// draw between from past to now.
	for (int i=changeBarNum-1; i>=0; i--) {
		switch (momentum) {

			case MOMENTUM_UP:
				currentHighValue	= High[i];
				currentLowValue		= Low[i];
				peakHighValue		= High[peakPoint];
				baseLowValue		= Low[basePoint];

				if (peakHighValue	< currentHighValue) {
					// momentum still continuing up
					// and exceed max point

					// reset old peak point
					ZigzagBuffer[peakPoint]	= 0.0;

					// set new peak point
					ZigzagBuffer[i]			= currentHighValue;

					if (i != 0) {
						// if not last bar then peak point update
						peakPoint			= i;
					} else {
						setZanteiVolume(basePoint, i, currentHighValue, momentum);	// i==0
					}
				} else {
					curMomentumLength = peakHighValue - baseLowValue;
					newMomentumLength = peakHighValue - currentLowValue;

					if ((newMomentumLength/basisWidth)*100 <= turningWidth) {
						// momentum still continuing up
						// however ascertain direction

						// reset peak point
						ZigzagBuffer[peakPoint]	= peakHighValue;

						// reset current point
						ZigzagBuffer[i]			= 0.0;

						setZanteiVolume(basePoint, peakPoint, peakHighValue, momentum);
					} else {
						// momentum changes to down from up

						// reset old peak point
						ZigzagBuffer[peakPoint]	= peakHighValue;

						// set new peak point
						ZigzagBuffer[i]			= currentLowValue;

						if (i != 0) {
							// set volume text
							setPeakVolume(peakPoint, peakHighValue, momentum);
							setAverageVolume(basePoint, peakPoint, momentum);

							// if not last bar then peak point update
							momentum	= MOMENTUM_DOWN;
							basePoint	= peakPoint;
							peakPoint	= i;
						} else {
							setZanteiVolume(basePoint, peakPoint, peakHighValue, momentum);
						}
					}
				}
				break;

			case MOMENTUM_NONE:
				currentHighValue	= High[i];
				currentLowValue		= Low[i];

				if (basePoint == 0) {
					// only first time
					// momentum still continuing none
					ArrayInitialize(ZigzagBuffer, 0.0);
					basePoint		= i;
				} else {
					baseHighValue	= High[basePoint];
					baseLowValue	= Low[basePoint];

					if (
						// exceed only high value
						baseHighValue	< currentHighValue
					 && baseLowValue	<= currentLowValue
					) {
						// momentum changes to up from none
						momentum				= MOMENTUM_UP;
						peakPoint				= i;

						ZigzagBuffer[basePoint]	= baseLowValue;
						ZigzagBuffer[peakPoint]	= currentHighValue;
					} else if (
						// exceed both
						(	baseHighValue	<= currentHighValue
						 &&	baseLowValue	>= currentLowValue)
						// not exceed both
					 || (	baseHighValue	>  currentHighValue
					 	 && baseLowValue	<  currentLowValue)
					) {
						// momentum still continuing none

					} else if (
						// exceed only low value
						baseHighValue 	>= currentHighValue
					 &&	baseLowValue	>  currentLowValue
					) {
						// momentum changes to down from none
						momentum				= MOMENTUM_DOWN;
						peakPoint				= i;

						ZigzagBuffer[basePoint]	= baseHighValue;
						ZigzagBuffer[peakPoint] = currentLowValue;
					}
				}
				break;

			case MOMENTUM_DOWN:
				currentHighValue	= High[i];
				currentLowValue		= Low[i];
				peakLowValue		= Low[peakPoint];
				baseHighValue		= High[basePoint];

				if (peakLowValue > currentLowValue) {
					// momentum still continuing down
					// and exceed min point

					// reset old peak point
					ZigzagBuffer[peakPoint]	= 0.0;

					// set new peak point
					ZigzagBuffer[i]			= currentLowValue;

					if (i != 0) {
						// if not last bar then point update peak 
						peakPoint			= i;
					} else {
						setZanteiVolume(basePoint, i, currentLowValue, momentum);	// i==0
					}
				} else {
					curMomentumLength = baseHighValue - peakLowValue;
					newMomentumLength = currentHighValue - peakLowValue;
					if ((newMomentumLength/basisWidth)*100 <= turningWidth) {
						// momentum still continuing up
						// however ascertain direction

						// reset peak point
						ZigzagBuffer[peakPoint]	= peakLowValue;

						// reset current point
						ZigzagBuffer[i]			= 0.0;

						setZanteiVolume(basePoint, peakPoint, peakLowValue, momentum);
					} else {
						// momentum changes to up from down

						// reset old peak point
						ZigzagBuffer[peakPoint]	= peakLowValue;

						// set new peak point
						ZigzagBuffer[i]			= currentHighValue;

						if (i != 0) {
							// set volume text
							setPeakVolume(peakPoint, peakLowValue, momentum);
							setAverageVolume(basePoint, peakPoint, momentum);

							// if not last bar then peak point update
							momentum	= MOMENTUM_UP;
							basePoint	= peakPoint;
							peakPoint	= i;
						} else {
							setZanteiVolume(basePoint, peakPoint, peakLowValue, momentum);
						}
					}
				}
				break;
		}
	}

Print("end start");
	return(0);
}

void setPeakVolume(int peakPoint, double peakPrice, int momentum) {

	double y;
	if (momentum == MOMENTUM_UP) {
		y = peakPrice + (((WindowPriceMax()-WindowPriceMin())/100.0)*3.5);
	} else {
		y = peakPrice;
	}

	string objName = textPeakVolumePrefix+(textPeakVolumeCount+1);
	if (ObjectCreate(objName, OBJ_TEXT, 0, Time[peakPoint], y)) {
		ObjectSetText(objName, getCardinalNumber(Volume[peakPoint]), 8, "", White);
		textPeakVolumeCount += 1;
	} else {
		int err = GetLastError();
		if (err == 4200) {
			ObjectDelete(objName);
			setPeakVolume(peakPoint, peakPrice, momentum);
		} else {
			Print("ERROR:" + err + ",Name:"+objName + ErrorDescription(err));
		}
	}
}

void setAverageVolume(int startPoint, int endPoint, int momentum) {

	double amount=0.0,barCount=startPoint-endPoint+1;
	int average;
	int middlePoint;

	for (int i=startPoint; i>=endPoint; i--) {
		amount = amount + Volume[i];
	}

	average = amount / barCount;
	middlePoint = endPoint+MathFloor(barCount/2);

	string objName = textAverageVolumePrefix+(textAverageVolumeCount+1);
	if (ObjectCreate(objName, OBJ_TEXT, 0, Time[middlePoint], High[middlePoint])) {
		ObjectSetText(objName, getCardinalNumber(average), 12, "", Red);
		if (momentum == MOMENTUM_UP) {
			ObjectSet(objName, OBJPROP_ANGLE, 45);
		} else {
			ObjectSet(objName, OBJPROP_ANGLE, -45);
		}
		textAverageVolumeCount += 1;
	} else {
		int err = GetLastError();
		if (err == 4200) {
			ObjectDelete(objName);
			setAverageVolume(startPoint, endPoint, momentum);
		} else {
			Print("ERROR:" + err + ",Name:"+objName + ErrorDescription(err));
		}
	}
}

void setZanteiVolume(int basePoint, int peakPoint, double peakPrice, int momentum) {
	delZanteiVolume();

	// zantei peak volume
	double y;
	if (momentum == MOMENTUM_UP) {
		y = peakPrice + (((WindowPriceMax()-WindowPriceMin())/100.0)*3.5);
	} else {
		y = peakPrice;
	}

	// get past time ratio in last(current) bar for use period
	double pastTimeRatio = (TimeCurrent() - Time[0]) / (Period()*60.0);
	double expectVolume = Volume[0] / pastTimeRatio;
	int ev = expectVolume;

	string objName = textPeakVolumePrefix+"A";
	if (ObjectCreate(objName, OBJ_TEXT, 0, Time[peakPoint], y)) {
		ObjectSetText(objName, getCardinalNumber(Volume[peakPoint]), 8, "", Yellow);
	} else {
		int err = GetLastError();
		Print("ERROR:" + err + ",Name:"+objName + ErrorDescription(err));
	}
	if (peakPoint != 0) {
		objName = textPeakVolumePrefix+"B";
		if (momentum == MOMENTUM_UP) {
			y = High[0] + (((WindowPriceMax()-WindowPriceMin())/100.0)*3.5);
		} else {
			y = Low[0];
		}
		if (ObjectCreate(objName, OBJ_TEXT, 0, Time[0], Close[0])) {
//		if (ObjectCreate(objName, OBJ_TEXT, 0, Time[0], y)) {
			ObjectSetText(objName, getCardinalNumber(Volume[0]) + "("+getCardinalNumber(ev)+")", 8, "", Yellow);
		} else {
			err = GetLastError();
			Print("ERROR:" + err + ",Name:"+objName + ErrorDescription(err));
		}
	}

	// zantei average volume
	double amount=0.0,barCount=basePoint-peakPoint+1;
	int average;
	int middlePoint;

	for (int i=basePoint; i>=peakPoint; i--) {
		amount = amount + Volume[i];
	}

	average = amount / barCount;
	middlePoint = peakPoint+MathFloor(barCount/2);

	objName = textAverageVolumePrefix+"A";
	if (ObjectCreate(objName, OBJ_TEXT, 0, Time[middlePoint], High[middlePoint])) {
		ObjectSetText(objName, getCardinalNumber(average), 12, "", Yellow);
		if (momentum == MOMENTUM_UP) {
			ObjectSet(objName, OBJPROP_ANGLE, 45);
		} else {
			ObjectSet(objName, OBJPROP_ANGLE, -45);
		}
	} else {
		err = GetLastError();
		Print("ERROR:" + err + ",Name:"+objName + ErrorDescription(err));
	}
}

void delFixedVolumes() {
	int allobjectsNum = ObjectsTotal();
	string tempName;

int delc=0;
bool f,s;
Print("start delete:"+allobjectsNum);
	for (int i=0; i<allobjectsNum; i++) {
		tempName = ObjectName(i);

f=		(StringSubstr(tempName, 0, textPeakVolumeLength) == textPeakVolumePrefix);
s= (StringSubstr(tempName, 0, textAverageVolumeLength) == textAverageVolumePrefix);
Print(i+",name:"+tempName+","+f+","+s );

		// delete peak volume labels
		if (StringSubstr(tempName, 0, textPeakVolumeLength) == textPeakVolumePrefix) {
			ObjectDelete(tempName);
			delc++;
		}

		// delete average volume labels
		if (StringSubstr(tempName, 0, textAverageVolumeLength) == textAverageVolumePrefix) {
			ObjectDelete(tempName);
			delc++;
		}
	}

	textPeakVolumeCount = 0;
	textAverageVolumeCount = 0;

Print("end delete:"+delc);
}

void delZanteiVolume() {
	ObjectDelete(textPeakVolumePrefix+"A");
	ObjectDelete(textPeakVolumePrefix+"B");
	ObjectDelete(textAverageVolumePrefix+"A");
	ObjectDelete(textAverageVolumePrefix+"B");
	ObjectDelete(textAverageVolumePrefix+"C");
}

string unit[] = {
 "",
 "K",
 "M",
 "G",
 "T",
 "P",
 "E"
};

string getCardinalNumber(int value) {
	int i;
	double v = value/1.0;
	int under=2;

	for (i=0; (v / 1000.0) >= 1.0; i++) {
		v = v/1000.0;
	}

	if (i==0) under=0;
	return (DoubleToStr(v, under)+unit[i]);
}

bool reset() {
	double currentMax	= WindowPriceMax();
	double currentMin	= WindowPriceMin();
	double currentWidth	= currentMax - currentMin;
	double changeWidth;

	if (currentWidth == 0) {
		changeWidth = 0.0;
	} else {
		changeWidth	= (basisWidth / currentWidth) * 100;
	}

	if (changeWidth <= 95 || 105 <= changeWidth) {
		// view area changing price max-min over 5%
		Print("do reset! old:"+basisWidth+",new:"+currentWidth+",change:"+changeWidth);
		deinit();
		init();

		return (true);
	} else {
		Print("no reset! old:"+basisWidth+",new:"+currentWidth+",change:"+changeWidth);
	}
	
	return (false);
}
//+------------------------------------------------------------------+


