#property indicator_chart_window

extern string note1 = "lowerTimeframe: 0=auto, 1,5,30,60,240,1440,10080";
extern int lowerTimeframe = 0;
extern double overboughtLevel = 69.0;
extern double oversoldLevel = 31.0;
extern bool showAlerts = true;
extern bool showDashedAlertSeparator = true;
extern bool sendEmail = false;
extern string emailSubject = "DynamiteHelper";
extern string note2 = "checkEveryTick: higher CPU usage, but should catch some edge cases";
extern bool checkEveryTick = false;
extern string note3 = "useOnlyPairs: comma-separated list of symbols, or blank to use all";
extern string useOnlyPairs = "";
extern bool useExactPairNames = false;

string pairsList[];
int pairsListLen = 0;

int ExtractNewItems(string prev_array[], string cur_array[], string out_array_new[], string out_array_existing[]) {
	int i, j;
	int k = 0, l = 0;
	bool found;
	int cur_array_size = ArraySize(cur_array);
	int prev_array_size = ArraySize(prev_array);
	ArrayResize(out_array_new, cur_array_size);
	ArrayResize(out_array_existing, cur_array_size);
	for (i = 0; i < cur_array_size; ++i) {
		found = false;
		for (j = 0; j < prev_array_size; ++j) {
			if (prev_array[j] == cur_array[i]) {
				found = true;
				break;
			}
		}
		if (!found) {
			out_array_new[k++] = cur_array[i];
		} else {
			out_array_existing[l++] = cur_array[i];
		}
	}
	return k;
}

string prev_matches[];
int num_prev_matches = 0;
bool showedAtStart = false;

void init() {
    ArrayResize(prev_matches, SymbolsTotal(false));
    
    string comma_str = ",";
    ushort comma_chr = StringGetCharacter(comma_str, 0);
    if (useOnlyPairs != "") {
        pairsListLen = StringSplit(useOnlyPairs, comma_chr, pairsList);
        int i;
        for (i = 0; i < pairsListLen; ++i) {
            pairsList[i] = StringTrimLeft(StringTrimRight(pairsList[i]));
            if (!useExactPairNames) StringToUpper(pairsList[i]);
        }
    }

    if (lowerTimeframe != 0 && (lowerTimeframe != 1 && lowerTimeframe != 5 && lowerTimeframe != 15 && 
        lowerTimeframe != 30 && lowerTimeframe != 60 && lowerTimeframe != 240 && lowerTimeframe != 1440 && lowerTimeframe != 10080)) {
        Alert("Invalid lowerTimeframe value: Must be 0,1,5,15,30,60,240,1440,10080");
        lowerTimeframe = 0;
    }
   if (lowerTimeframe == 0) {
      switch (Period()) {
       case PERIOD_M1:
        case PERIOD_M5: lowerTimeframe = PERIOD_M1; break;
        case PERIOD_M15: lowerTimeframe = PERIOD_M5; break;
        case PERIOD_M30:
        case PERIOD_H1: lowerTimeframe = PERIOD_M15; break;
        case PERIOD_H4: lowerTimeframe = PERIOD_H1; break;
        case PERIOD_D1: lowerTimeframe = PERIOD_H4; break;
        case PERIOD_W1: lowerTimeframe = PERIOD_D1; break;
        case PERIOD_MN1: lowerTimeframe = PERIOD_W1; break;
        default: lowerTimeframe = 0;
    }
  }
}

bool StringContainsAStringInList(string str, string sslist[], int ssllen, bool exact) {
    int i;
     for (i = 0; i < ssllen; ++i) {
         if (exact && str == sslist[i]) {
             return true;
         } else if (!exact && StringFind(str, sslist[i]) > -1) {
             return true;
         }
     }
     return false;
}

void start() {
   string cur_matches[];
   string new_matches[];
   string unchanged_matches[];
   double cls, ma, stoch;
   string sym, symUC;
   string text;
   int i, n = SymbolsTotal(false);
   int k = 0;
   int numNew;
   
   if (showedAtStart && checkEveryTick==false && iVolume(NULL, lowerTimeframe, 0) > 1) return;
   
   ArrayResize(cur_matches, n);
   for (i = 0; i < n; ++i) {
	   sym = SymbolName(i, false);
	   symUC = sym;
	   if (!useExactPairNames) StringToUpper(symUC);
      if (pairsListLen > 0 && StringContainsAStringInList(symUC, pairsList, pairsListLen, useExactPairNames) == false) continue;
	   cls = iClose(sym, lowerTimeframe, 0);
	   ma = iMA(sym, lowerTimeframe, 200, 0, MODE_EMA, PRICE_CLOSE, 0);
	   stoch = iStochastic(sym, 0, 10, 3, 3, MODE_LWMA, 1, MODE_MAIN, 0);
	   if ((cls >= ma && stoch <= oversoldLevel) || (cls <= ma && stoch >= overboughtLevel)) {
		   cur_matches[k++] = sym;
	   }
   }
   
   numNew = ExtractNewItems(prev_matches, cur_matches, new_matches, unchanged_matches);
   string emailMsg = "";
   string alertStr = "";
   int numUnch = k - numNew;
   if (numNew > 0) {
         if (showAlerts && showDashedAlertSeparator)  Alert("-------------------");
         if (numUnch > 0) {
	          alertStr = "Unchanged: ";
	        for (i = 0; i < numUnch; ++i) {
	   	     alertStr += unchanged_matches[i];
	      	   if (i < numUnch - 1) alertStr += ", ";
	         }
	      if (showAlerts) Alert(alertStr);
	      if (sendEmail) {
	         	emailMsg = alertStr + "\r\n";
	      }
    }
	 
	   alertStr = "New: ";
	   for (i = 0; i < numNew; ++i) {
		   alertStr += new_matches[i];
		   if (i < numNew-1) alertStr += ", ";
	   }
	   if (showAlerts) Alert(alertStr);
	   if (sendEmail) {
		   emailMsg += alertStr;
		   SendMail(emailSubject, emailMsg);
	   } 
   }
   ArrayCopy(prev_matches, cur_matches);
   num_prev_matches = k;
   showedAtStart = true;
}
