//
//  "00-fxStrength_v102" -- show strength of individual currency (JPY/USD/EUR/GBP/CHF/AUD/NZD/CAD)
//
//    Ver. 1.00  2008/9/27(Sat)   initial version
//    Ver. 1.01  2008/10/12(Sun)  added speed meter
//    Ver. 1.02  2008/12/02(Tue)  bShowOnlyMeter
//               2010/05/20(Thu)  window
// 
// 
#property copyright  "00mql4@gmail.com"
#property link       "http://00mql4.blogspot.com/"

//---- indicator settings
#property  indicator_separate_window

#property  indicator_buffers  8

#property  indicator_color1  Red
#property  indicator_color2  White
#property  indicator_color3  Aqua
#property  indicator_color4  Yellow
#property  indicator_color5  Magenta
#property  indicator_color6  Lime
#property  indicator_color7  Green
#property  indicator_color8  DodgerBlue

#property  indicator_width1  1
#property  indicator_width2  1
#property  indicator_width3  1
#property  indicator_width4  1
#property  indicator_width5  1
#property  indicator_width6  1
#property  indicator_width7  1
#property  indicator_width8  1

#property  indicator_style1  STYLE_DOT
#property  indicator_style2  STYLE_DOT
#property  indicator_style3  STYLE_DOT
#property  indicator_style4  STYLE_DOT
#property  indicator_style5  STYLE_DOT
#property  indicator_style6  STYLE_DOT
#property  indicator_style7  STYLE_DOT
#property  indicator_style8  STYLE_DOT

//---- defines
#define N_FX_MAX    8
#define N_PAIR_MAX  28  // = 8C2

#define JPY  0
#define USD  1
#define EUR  2
#define GBP  3
#define CHF  4
#define AUD  5
#define NZD  6
#define CAD  7

//---- indicator parameters
extern int    timeFrame          = 0;            // time frame
extern bool   bShowOnlyMeter     = false;        // show only speed meter
extern int    nAve               = 3;            // period of average
extern int    nShift             = 0;            // shift
extern int    labelCorner        = 1;            // 0: top-left, 1: top-right, 2: bottom-left, 3: bottom-right
extern string _help_appliedPrice = "(0:CLOSE 1:OPEN 2:HI 3:LOW 4:MED 5:TYP 6:WEIGHTED)";
extern int    appliedPrice       = PRICE_CLOSE;  // 0: CLOSE, 1: OPEN, 2: HIGH, 3: LOW, 4: MEDIAN, 5: TYPICAL, 6: WEIGHTED
extern string _help_maMethod     = "(0:SMA 1:EMA 2:SMMA 3:LWMA)";
extern int    maMethod           = MODE_EMA;     // 0: MODE_SMA, 1: MODE_EMA, 2: MODE_SMMA, 3: MODE_LWMA
extern bool   bReverse           = false;        // draw upside down
extern bool   bUseMinorPair      = false;        // use not only JPY/USD/EUR/GBP/CHF but also AUD/NZD/CAD
extern bool   bIgnoreNoPair      = false;        // ignore symbols in pair not on server
extern bool   bAll               = true;         // show all
extern bool   bJPY               = true;         // show JPY
extern bool   bUSD               = true;         // show USD
extern bool   bEUR               = true;         // show EUR
extern bool   bGBP               = false;        // show GBP
extern bool   bCHF               = false;        // show CHF
extern bool   bAUD               = false;        // show AUD
extern bool   bNZD               = false;        // show NZD
extern bool   bCAD               = false;        // show CAD
extern string refDate            = "2008/08/08 08:00:00";  // reference date, valid when bUseRefData is true
extern bool   bUseRefDate        = false;        // reset on refDate
extern bool   bResetWindow       = true;         // reset in left size of window
extern bool   bResetHourly       = false;        // reset hourly
extern bool   bResetDaily        = false;        // reset daily
extern int    tResetOffsetHour   = 8;            // offset hour, valid when bResetDaily is true
extern bool   bResetWeekly       = false;        // reset weekly
extern bool   bResetMonthly      = false;        // reset monthly
extern bool   bRemarkCurPair     = true;         // draw symbols with following style
extern int    styleCurPair       = STYLE_SOLID;  // style for symbols in current pair
extern int    styleOthers        = STYLE_DOT;    // style for symbols not in current pair
extern int    widthCurPair       = 1;            // line width for symbols in current pair
extern int    widthOthers        = 1;            // line width for symbols not in current pair
extern int    window             = -1;           // sub window No., -1: auto

//---- indicator buffers
double BufferJPY[];
double BufferUSD[];
double BufferEUR[];
double BufferGBP[];
double BufferCHF[];
double BufferAUD[];
double BufferNZD[];
double BufferCAD[];

//---- vars
string sIndicatorName = "00-fxStrength_v102";
int    N_FX;
int    N_PAIR;
color  colJPY = Red;
color  colUSD = White;
color  colEUR = Aqua;
color  colGBP = Yellow;
color  colCHF = Magenta;
color  colAUD = Lime;
color  colNZD = Green;
color  colCAD = DodgerBlue;
//
string g_font0 = "Arial";
string g_font1 = "Arial Black";
string g_fontMeter = "Courier New";
int    g_fontSize  = 10;
color  g_colMeterBase = Black;
//
string g_sLabelFx[N_FX_MAX] = {
    "00-fxStrength JPY",
    "00-fxStrength USD",
    "00-fxStrength EUR",
    "00-fxStrength GBP",
    "00-fxStrength CHF",
    "00-fxStrength AUD",
    "00-fxStrength NZD",
    "00-fxStrength CAD"
};
string g_sFx[N_FX_MAX]  = {
    "JPY",
    "USD",
    "EUR",
    "GBP",
    "CHF",
    "AUD",
    "NZD",
    "CAD"
};
string g_sPair[N_PAIR_MAX][2] = {
    // major pairs
    // JPY
    "JPY", "USDJPY",  //  0
    "JPY", "EURJPY",  //  1
    "JPY", "GBPJPY",  //  2
    "JPY", "CHFJPY",  //  3
    // USD		   
    "USD", "EURUSD",  //  7
    "USD", "GBPUSD",  //  8
    "USD", "USDCHF",  //  9
    // EUR		   
    "EUR", "EURGBP",  // 13
    "EUR", "EURCHF",  // 14
    // GBP		   
    "GBP", "GBPCHF",  // 18
    // CHF
    // none
    
    // minor pairs
    // JPY
    "JPY", "AUDJPY",  //  4
    "JPY", "NZDJPY",  //  5
    "JPY", "CADJPY",  //  6
    // USD
    "USD", "AUDUSD",  // 10
    "USD", "NZDUSD",  // 11
    "USD", "USDCAD",  // 12
    // EUR
    "EUR", "EURAUD",  // 15
    "EUR", "EURNZD",  // 16
    "EUR", "EURCAD",  // 17
    // GBP
    "GBP", "GBPAUD",  // 19
    "GBP", "GBPNZD",  // 20
    "GBP", "GBPCAD",  // 21
    // CHF
    "CHF", "AUDCHF",  // 22
    "CHF", "NZDCHF",  // 23
    "CHF", "CADCHF",  // 24
    // AUD
    "AUD", "AUDNZD",  // 25
    "AUD", "AUDCAD",  // 26
    // NZD
    "NZD", "NZDCAD"  // 27
    // CAD
    // none
};

bool     g_bShowFx[N_FX_MAX];
color    g_colFx[N_FX_MAX];
double   g_fx[N_FX_MAX];
double   g_pair[N_PAIR_MAX][2];
double   g_pair0[N_PAIR_MAX][2];
bool     g_validFx[N_FX_MAX];
int      g_window;

//----------------------------------------------------------------------
bool check(datetime t = 0)
{
    if (t <= 0) {
	return;
    }
    
    bool bOk = true;
    string s;
    
    for (int i = 0; i < N_PAIR; i++) {
	string base = g_sPair[i][0];
	string sym  = g_sPair[i][1];
	if (StringFind(sym, base) < 0) {
	    s = "Error, illegal g_sPair[" + i + "]= " + base + ", " + sym;
	    Print(s);
	    bOk = false;
	}
	int iBar = iBarShift(sym, timeFrame, t);
	double c = iClose(sym, timeFrame, iBar + 1);
	if (c <= 0) {
	    Print("Warning, t= ", TimeToStr(t), "(", t, "), TF= ", timeFrame,
		  ", iClose(", sym, ")= ", c, "@", TimeToStr(iTime(sym, timeFrame, iBar)));
	}
    }
    
    return(bOk);
}

//----------------------------------------------------------------------
void init()
{
    if (bUseMinorPair) {
	N_FX = 8;     // 8 pairs, JPY/USD/EUR/GBP/CHF/AUD/NZD/CAD
	N_PAIR = 28;  // 8C2
    } else {
	N_FX = 5;     // 5 pairs, JPY/USD/EUR/GBP/CHF
	N_PAIR = 10;  // 5C2
    }
    
    check();
    
    SetLevelValue(0, 0.00);
    SetLevelValue(1, 1.00);
    
    for (int iFx = 0; iFx < N_FX; iFx++) {
	SetIndexStyle(iFx, DRAW_LINE);
	SetIndexDrawBegin(iFx, nAve);
	SetIndexLabel(iFx, g_sFx[iFx]);
    }
    
    SetIndexBuffer(0, BufferJPY);
    SetIndexBuffer(1, BufferUSD);
    SetIndexBuffer(2, BufferEUR);
    SetIndexBuffer(3, BufferGBP);
    SetIndexBuffer(4, BufferCHF);
    SetIndexBuffer(5, BufferAUD);
    SetIndexBuffer(6, BufferNZD);
    SetIndexBuffer(7, BufferCAD);
    
    IndicatorShortName(sIndicatorName);
    
    g_bShowFx[JPY] = (bJPY || bAll);
    g_bShowFx[USD] = (bUSD || bAll);
    g_bShowFx[EUR] = (bEUR || bAll);
    g_bShowFx[GBP] = (bGBP || bAll);
    g_bShowFx[CHF] = (bCHF || bAll);
    g_bShowFx[AUD] = ((bAUD || bAll) && bUseMinorPair);
    g_bShowFx[NZD] = ((bNZD || bAll) && bUseMinorPair);
    g_bShowFx[CAD] = ((bCAD || bAll) && bUseMinorPair);
    
    if (!bShowOnlyMeter) {
	g_colFx[JPY] = colJPY;
	g_colFx[USD] = colUSD;
	g_colFx[EUR] = colEUR;
	g_colFx[GBP] = colGBP;
	g_colFx[CHF] = colCHF;
	g_colFx[AUD] = colAUD;
	g_colFx[NZD] = colNZD;
	g_colFx[CAD] = colCAD;
    } else {
	g_colFx[JPY] = -1;
	g_colFx[USD] = -1;
	g_colFx[EUR] = -1;
	g_colFx[GBP] = -1;
	g_colFx[CHF] = -1;
	g_colFx[AUD] = -1;
	g_colFx[NZD] = -1;
	g_colFx[CAD] = -1;
    }
}

//----------------------------------------------------------------------
string getFxLabelName(int iFx)
{
    return(g_sLabelFx[iFx] + " name" + g_window);
}

//----------------------------------------------------------------------
string getMeterBaseLabelName(int iFx)
{
    return(g_sLabelFx[iFx] + " meter0Base" + g_window);
}

//----------------------------------------------------------------------
string getMeterLabelName(int iFx)
{
    return(g_sLabelFx[iFx] + " meter1Meter" + g_window);
}

//----------------------------------------------------------------------
int deinit()
{
    for (int iFx = 0; iFx < N_FX; iFx++) {
	ObjectDelete(getFxLabelName(iFx));
	ObjectDelete(getMeterBaseLabelName(iFx));
	ObjectDelete(getMeterLabelName(iFx));
    }
}

//----------------------------------------------------------------------
void updateRate(datetime t)
{
    for (int iPair = 0; iPair < N_PAIR; iPair++) {
	string sym = g_sPair[iPair][1];
	int x = iBarShift(sym, timeFrame, t);
	double p0 = 0, p1 = 0;
	p0 = iMA(sym, timeFrame, MathMax(nAve, 1), 0, maMethod, appliedPrice, x + nShift);
	if (p0 > 0) {
	    p1 = 1.0 / p0;
	}
	
	g_pair[iPair][0] = p0;
	g_pair[iPair][1] = p1;
    }

    for (int iFx = 0; iFx < N_FX; iFx++) {    
	g_validFx[iFx] = true;
    }
    
    double tot;
    for (iFx = 0; iFx < N_FX; iFx++) {
	tot = 0.0;
	for (iPair = 0; iPair < N_PAIR; iPair++) {
	    int k = StringFind(g_sPair[iPair][1], g_sFx[iFx]);
	    if (k == 0 || k == 3) {
		if (k == 0) {
		    p0 = g_pair0[iPair][1];
		    p1 = g_pair[iPair][1];
		} else {
		    p0 = g_pair0[iPair][0];
		    p1 = g_pair[iPair][0];
		}
		if (p0 == 0) {
		    g_validFx[iFx] = false;
		    if (!bIgnoreNoPair) {
			tot = 1.0;
			break;
		    }
		} else {
		    tot += p1 / p0;
		}
	    }
	}
	g_fx[iFx] = tot;
    }
}

//----------------------------------------------------------------------
int TimeStartDayOfYear(datetime t)
{
    int y = TimeYear(t);
    int dow0 = TimeDayOfYear(StrToTime(y + "." + "1.1"));  // 0: Sunday, 1:Monday, ..., 6: Saturday
    
    return(dow0);
}

//----------------------------------------------------------------------
int TimeWeek(datetime t)
{
    int doy = TimeDayOfYear(t);  // 1: 1/1, 2: 1/2, 3: 1/3, ..., 365(6): 12/31
    int w = (doy + TimeStartDayOfYear(t)) / 7;
    
    return(w);
}

//----------------------------------------------------------------------
void start()
{
    int limit;
    int counted_bars = IndicatorCounted();
    
    if (counted_bars > 0) {
	counted_bars--;
    }
    
    limit = Bars - counted_bars;
    
    double fx0[N_FX_MAX];
    int monthLast = -1;
    int weekLast = -1;
    int dayLast = -1;
    int hourLast = -1;
    int xs = limit - 1;
    int xe = 0;
    int xs0 = 0;
    int n = WindowBarsPerChart();
    
    if (bResetWindow) {
	xs0 = WindowFirstVisibleBar();
	xs = xs0 + n;
	xe = xs - n * 3;
    }
    if (xe < 0) {
	xe = 0;
    }
    
    double dRatio[N_FX_MAX];
    
    for (int iBar = xs; iBar >= xe; iBar--) {
	bool bPeriodChanged = false;
	bool bInit = false;
	
	datetime t = Time[iBar];
	datetime t1 = Time[iBar + 1];
	datetime tStart;
	updateRate(t);
	
	if (bUseRefDate && refDate != "") {
	    tStart = StrToTime(refDate);
	    bInit = true;
	} else if (bResetWindow) {
	    tStart = Time[xs0];
	    if (iBar == xs) {
		bInit = true;
	    }
	} else {
	    // reset monthly
	    int month = TimeMonth(t);
	    if (bResetMonthly && month != monthLast) {
		tStart = StrToTime(TimeYear(t) + "." + TimeMonth(t) + ".1 0:0:0");
		bInit = true;
		monthLast = month;
		if (month >= 0 && month != TimeMonth(t1)) {
		    bPeriodChanged = true;
		}
	    }
	    
	    // reset weekly
	    int week = TimeWeek(t);
	    if (bResetWeekly && week != weekLast) {
		tStart = StrToTime(TimeYear(t) + ".1.1") + (week * 7 - TimeStartDayOfYear(t)) * 24 * 60 * 60;
		bInit = true;
		weekLast = week;
		if (week >= 0 && week != TimeWeek(t1)) {
		    bPeriodChanged = true;
		}
	    }
	    
	    // reset daily
	    int day = TimeDay(t);
	    if (bResetDaily && day != dayLast) {
		tStart = (MathFloor(t / (24 * 60 * 60)) * 24 + tResetOffsetHour) * 60 * 60;
		bInit = true;
		dayLast = day;
		if (day >= 0 && day != TimeDay(t1)) {
		    bPeriodChanged = true;
		}
	    }
	    
	    // reset hourly
	    int hour = TimeHour(t);
	    if (bResetHourly && hour != hourLast) {
		tStart = MathFloor(t / (60 * 60)) * 60 * 60;
		bInit = true;
		hourLast = hour;
		if (hour >= 0 && hour != TimeHour(t1)) {
		    bPeriodChanged = true;
		}
	    }
	}
	
	if (bInit) {
	    bInit = false;
	    check(tStart);
	    updateRate(tStart);
	    for (int iPair = 0; iPair < N_PAIR; iPair++) {
		g_pair0[iPair][0] = g_pair[iPair][0];
		g_pair0[iPair][1] = g_pair[iPair][1];
	    }
	    updateRate(tStart);
	    for (int iFx = 0; iFx < N_FX; iFx++) {
		fx0[iFx] = g_fx[iFx];
	    }
	    updateRate(t);
	}
	
	double ratio[N_FX_MAX];
	
	int sign = -1;
	int ofst = 2;
	if (bReverse) {
	    sign = 1;
	    ofst = 0;
	}
	for (iFx = 0; iFx < N_FX_MAX; iFx++) {
	    double r = EMPTY_VALUE;
	    if (iFx < N_FX) {
		if (g_bShowFx[iFx] && !bPeriodChanged && fx0[iFx] != 0) {
		    r = sign * g_fx[iFx] / fx0[iFx] + ofst;
		}
	    }
	    ratio[iFx] = r;
	}
	
	int i = 0;
	BufferJPY[iBar] = ratio[i]; i++;
	BufferUSD[iBar] = ratio[i]; i++;
	BufferEUR[iBar] = ratio[i]; i++;
	BufferGBP[iBar] = ratio[i]; i++;
	BufferCHF[iBar] = ratio[i]; i++;
	BufferAUD[iBar] = ratio[i]; i++;
	BufferNZD[iBar] = ratio[i]; i++;
	BufferCAD[iBar] = ratio[i]; i++;
	
	dRatio[JPY] = BufferJPY[iBar] - BufferJPY[iBar + 1];
	dRatio[USD] = BufferUSD[iBar] - BufferUSD[iBar + 1];
	dRatio[EUR] = BufferEUR[iBar] - BufferEUR[iBar + 1];
	dRatio[GBP] = BufferGBP[iBar] - BufferGBP[iBar + 1];
	dRatio[CHF] = BufferCHF[iBar] - BufferCHF[iBar + 1];
	dRatio[AUD] = BufferAUD[iBar] - BufferAUD[iBar + 1];
	dRatio[NZD] = BufferNZD[iBar] - BufferNZD[iBar + 1];
	dRatio[CAD] = BufferCAD[iBar] - BufferCAD[iBar + 1];
	
	int imin1 = 0;
	int imax1 = 0;
	double drMin1 = dRatio[imin1];
	double drMax1 = dRatio[imax1];
	for (iFx = 1; iFx < N_FX_MAX; iFx++) {
	    if (dRatio[iFx] < drMin1) {
		drMin1 = dRatio[iFx];
		imin1 = iFx;
	    }
	    if (dRatio[iFx] > drMax1) {
		drMax1 = dRatio[iFx];
		imax1 = iFx;
	    }
	}
    }
    
    if (window == -1) {
	g_window = WindowFind(sIndicatorName);
    } else {
	g_window = window;
    }
    int tx = 5;
    int ty = 20;
    if (labelCorner != 0) {
	ty = 5;
    }
    
    for (iFx = 0; iFx < N_FX_MAX; iFx++) {
	string sLabelName = getFxLabelName(iFx);
	string sLabelMeterBase = getMeterBaseLabelName(iFx);
	string sLabelMeter = getMeterLabelName(iFx);
	ObjectCreate(sLabelName, OBJ_LABEL, g_window, 0, 0);
	ObjectCreate(sLabelMeterBase, OBJ_LABEL, g_window, 0, 0);
	ObjectCreate(sLabelMeter, OBJ_LABEL, g_window, 0, 0);
	if (g_bShowFx[iFx]) {
	    bool bShownInChart = (StringFind(Symbol(), g_sFx[iFx]) >= 0);
	    string sFxName = g_sFx[iFx] + "  ";
	    string sMeterBase = ">>>>>>>>>>>";
	    string sMeter = "";
	    string fontName = g_font0;
	    if (bShownInChart) {
		fontName = g_font1;
	    }
	    
	    // meter
	    int nRatio = 0;
	    if (dRatio[imax1] != 0) {
		nRatio = MathRound(dRatio[iFx] / dRatio[imax1] * 5);
	    }
	    string sPointer = ">";
	    if (!bIgnoreNoPair && !g_validFx[iFx]) {
		sPointer = "-";
	    }
	    for (int k = -5; k <= 5; k++) {
		if (k == 0) {
		    sMeter = sMeter + "|";
		} else if (k <= nRatio) {
		    sMeter = sMeter + sPointer;
		} else {
		    sMeter = sMeter + " ";
		}
	    }
	    
	    // label
	    ObjectSetText(sLabelName, sFxName, g_fontSize, fontName, g_colFx[iFx]);
	    ObjectSet(sLabelName, OBJPROP_CORNER, labelCorner);
	    ObjectSet(sLabelName, OBJPROP_XDISTANCE, tx);
	    ObjectSet(sLabelName, OBJPROP_YDISTANCE, ty);
	    
	    // meter base
	    ObjectSetText(sLabelMeterBase, sMeterBase, g_fontSize, g_font1, g_colMeterBase);
	    ObjectSet(sLabelMeterBase, OBJPROP_CORNER, labelCorner);
	    ObjectSet(sLabelMeterBase, OBJPROP_XDISTANCE, tx + 50);
	    ObjectSet(sLabelMeterBase, OBJPROP_YDISTANCE, ty);
	    
	    // meter
	    ObjectSetText(sLabelMeter, sMeter, g_fontSize, g_fontMeter, g_colFx[iFx]);
	    ObjectSet(sLabelMeter, OBJPROP_CORNER, labelCorner);
	    ObjectSet(sLabelMeter, OBJPROP_XDISTANCE, tx + 50);
	    ObjectSet(sLabelMeter, OBJPROP_YDISTANCE, ty);
	    
	    ty += g_fontSize * 1.25;
	    
	    if (bRemarkCurPair) {
		if (bShownInChart) {
		    SetIndexStyle(iFx, DRAW_LINE, styleCurPair, widthCurPair);
		} else {
		    SetIndexStyle(iFx, DRAW_LINE, styleOthers, widthOthers);
		}
	    }
	    
	} else {
	    ObjectSet(sLabelName, OBJPROP_YDISTANCE, -1);
	    ObjectSet(sLabelMeterBase, OBJPROP_YDISTANCE, -1);
	    ObjectSet(sLabelMeter, OBJPROP_YDISTANCE, -1);
	}
    }
    
    WindowRedraw();
}
