//+------------------------------------------------------------------+ //| RenkoLiveChart_v600.4.2.mq4 | //| Copyright 2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ //+---------------------------------------------------------------------------+ //| EA VERSION //| RenkoLiveChart_v3.2.mq4 //| Inspired from Renko script by "e4" (renko_live_scr.mq4) //| Copyleft 2009 LastViking //| //| Aug 12 2009 (LV): //| - Wanted volume in my Renko chart so I wrote my own script //| //| Aug 20-21 2009 (LV) (v1.1 - v1.3): //| - First attempt at live Renko brick formation (bugs O bugs...) //| - Fixed problem with strange symbol names at some 5 digit //| brokers (credit to Tigertron) //| //| Aug 24 2009 (LV) (v1.4): //| - Handle High / Low in history in a reasonable way (prev. //| used Close) //| //| Aug 26 2009 (Lou G) (v1.5/v1.6): //| - Finaly fixing the "late appearance" (live Renko brick //| formation) bug //| //| Aug 31 2009 (LV) (v2.0): //| - Not a script anylonger, but run as indicator //| - Naroved down the MT4 bug that used to cause the "late appearance bug" //| a little closer (has to do with High / Low gaps) //| - Removed the while ... sleep() loop. Renko chart is now tick //| driven: -MUCH nicer to system resources this way //| //| Sep 03 2009 (LV) (v2.1): //| - Fixed so that Time[] holds the open time of the renko //| bricks (prev. used time of close) //| //| Sep 16 2009 (Lou G) (v3.0): //| - Optional wicks added //| - Conversion back to EA //| - Auto adjust for 5 and 6 dec brokers added //| enter RenkoBoxSize as "actual" size e.g. "10" for 10 pips //| - Compensation for "zero compare" problem added //| //| Okt 05 2009 (LV) (v3.1): //| - Fixed a bug related to BoxOffset //| - Auto adjust for 3 and 4 dec JPY pairs //| - Removed init() function //| - Changed back to old style Renko brick formation //| //| Okt 13 2009 (LV) (v3.2): //| - Added "EmulateOnLineChart" option (credit to Skipperxit/Mimmo) //| //| Feb 07 2014 (KZ) (v600.1): //| - Build 600 forced upgrade for new history file format //| - Build 600 has some "funny" quirks //| - "So that is why everyone is laughing"? //| - 1) The share/lock access rights //| - 2) It appears to me static variables do not act right //| - Seems like they might not be reset when the code is re-loaded //| - Not sure, so just moved them all to common //| - Many thanks to LastViking, Lou G, Kiads, Mihailo, and many others //| - that have contributed to making Renko a success //| - My apologies to each of you if I have abused your code //| - Many thanks to Mary-Jane for helping me get up to date on passing strings to DLLs //| - "surprise, surprise, surprise" //| - Many thanks to MetaQuirks for forcing me to bring my programming skills up to date //| //| Apr 03 2015 (SK) (v600.3) //| - Fixed missing wicks on the same side of bar as current direction //| - Fixed missing wicks on extremums //| - Made a start timer to build the chart once even if no ticks coming (i.e. during weekend) //| Nov 25 2015 (SK - https://www.mql5.com/en/users/marketeer) (v600.4) //| - Classical 'look and feel' of renko wicks applied as a patch //| //| Jan 19 2106 (File45 - https://www.mql5.com/en/users/file45/publications) (v600.5 & v600.6) //| - Transcribed to Post MT4 Build 600 New Code //| - Change Renko BoxSize from int to double //| - Corrected ShowWicks=false failure to remove wicks //| Mar 24 2106 (File45 - https://www.mql5.com/en/users/file45/publications) (v600.6) //| - Convert EA to Indicator //| //| From the MQL4 Reference //| MqlRates //| //| This structure stores information about the prices, volumes and spread. //| //| struct MqlRates //| { //| datetime time; // Period start time //| double open; // Open price //| double high; // The highest price of the period //| double low; // The lowest price of the period //| double close; // Close price //| long tick_volume; // Tick volume //| int spread; // Spread //| long real_volume; // Trade volume //| }; //+---------------------------------------------------------------------------+ #property copyright "Copyright 2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "6.00" #property strict #property indicator_chart_window #include #include //+------------------------------------------------------------------+ #import "user32.dll" int RegisterWindowMessageW(string lpString); #import //+------------------------------------------------------------------+ input double RenkoBoxSize = 3.0; input int RenkoBoxOffset = 0; input int RenkoTimeFrame = 7; // Offline Timeframe input bool ShowWicks = false; input bool EmulateOnLineChart = true; input bool StrangeSymbolName = false; //+------------------------------------------------------------------+ int HstHandle = -1, LastFPos = 0, MT4InternalMsg = 0; string sSymbolName; datetime dtSendTime; double dSendOpen; double dSendHigh; double dSendLow; double dSendClose; double dSendVol; bool bStopAll = false; int iRcdCnt = 0; int hwnd = 0; double BoxPoints, UpWick, DnWick; double PrevLow, PrevHigh, PrevOpen, PrevClose, CurVolume, CurLow, CurHigh, CurOpen, CurClose; datetime PrevTime; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { if(HstHandle > 0) { FileClose(HstHandle); } HstHandle = -1; EventSetTimer(1); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { if(HstHandle >= 0) { FileClose(HstHandle); HstHandle = -1; } Comment(""); return; } //+------------------------------------------------------------------+ //| 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[]) { if(bStopAll) return(0); //+------------------------------------------------------------------+ // This is only executed once, then the first tick arives. if(HstHandle < 0) { // Init // Error checking if(!IsConnected()) { Print("Waiting for connection..."); return(0); } if(!IsDllsAllowed()) { Print("Error: Dll calls must be allowed!"); bStopAll = true; return(0); } if(MathAbs(RenkoBoxOffset) >= RenkoBoxSize) { Print("Error: |RenkoBoxOffset| should be less then RenkoBoxSize!"); bStopAll = true; return(0); } switch(RenkoTimeFrame) { case 1: case 5: case 15: case 30: case 60: case 240: case 1440: case 10080: case 43200: case 0: Print("Error: Invald time frame used for offline renko chart (RenkoTimeFrame)!"); bStopAll = true; return(0); } double BoxSize = RenkoBoxSize; int BoxOffset = RenkoBoxOffset; if(Digits == 5 || (Digits == 3 && StringFind(Symbol(), "JPY") != -1)) { BoxSize = BoxSize * 10; BoxOffset = BoxOffset * 10; } if(Digits == 6 || (Digits == 4 && StringFind(Symbol(), "JPY") != -1)) { BoxSize = BoxSize * 100; BoxOffset = BoxOffset * 100; } if(StrangeSymbolName) sSymbolName = StringSubstr(Symbol(), 0, 6); else sSymbolName = Symbol(); BoxPoints = NormalizeDouble(BoxSize * Point, Digits); PrevLow = NormalizeDouble(BoxOffset * Point + MathFloor(Close[Bars - 1] / BoxPoints) * BoxPoints, Digits); DnWick = PrevLow; PrevHigh = PrevLow + BoxPoints; UpWick = PrevHigh; PrevOpen = PrevLow; PrevClose = PrevHigh; CurVolume = 1; PrevTime = Time[Bars - 1]; // create / open hst file HstHandle = FileOpenHistory(sSymbolName + IntegerToString(RenkoTimeFrame) + ".hst", FILE_BIN | FILE_WRITE | FILE_ANSI);//W1 if(HstHandle < 0) { Print("Error: can\'t create / open history file: " + ErrorDescription(GetLastError()) + ": " + sSymbolName + IntegerToString(RenkoTimeFrame) + ".hst"); bStopAll = true; return(0); } else { Print("History file opened for write, handle: " + IntegerToString(HstHandle)); FileSeek(HstHandle, 0, SEEK_SET); // Hist file opened as write zero length to create } // the file is created empty, now re-open it read write shared DoOpenHistoryReadWrite(); FileSeek(HstHandle, 0, SEEK_SET); //Hist file opened as read write for adding to // write hst file header - does anyone have a structure for this heading? int HstUnused[13]; FileWriteInteger(HstHandle, 401, LONG_VALUE); // Version // was 400 FileWriteString(HstHandle, "", 64); // Copyright FileWriteString(HstHandle, sSymbolName, 12); // Symbol FileWriteInteger(HstHandle, RenkoTimeFrame, LONG_VALUE); // Period FileWriteInteger(HstHandle, Digits, LONG_VALUE); // Digits FileWriteInteger(HstHandle, 0, LONG_VALUE); // Time Sign FileWriteInteger(HstHandle, 0, LONG_VALUE); // Last Sync FileWriteArray(HstHandle, HstUnused, 0, 13); // Unused // process historical data int i = Bars - 2; //Print(Symbol() + " " + High[i] + " " + Low[i] + " " + Open[i] + " " + Close[i]); //--------------------------------------------------------------------------- while(i >= 0) { CurVolume = CurVolume + Volume[i]; UpWick = MathMax(UpWick, High[i]); DnWick = MathMin(DnWick, Low[i]); // update low before high or the revers depending on is closest to prev. bar bool UpTrend = High[i] + Low[i] > High[i + 1] + Low[i + 1]; bool WipeUpWick = false, WipeDownWick = false; // go down in up phase while(UpTrend && (Low[i] < PrevLow - BoxPoints || CompareDoubles(Low[i], PrevLow - BoxPoints))) { PrevHigh = PrevHigh - BoxPoints; PrevLow = PrevLow - BoxPoints; PrevOpen = PrevHigh; PrevClose = PrevLow; dtSendTime = PrevTime; dSendOpen = PrevOpen; if(ShowWicks==true && DnWick < PrevLow && Low[i] >= PrevLow - BoxPoints) { dSendLow = DnWick; WipeDownWick = true; } else { //dSendLow = PrevLow;// When ShowWicks = false this code will show wicks (file45) dSendLow = PrevHigh; } if(ShowWicks==true && UpWick > PrevHigh && UpWick <= PrevHigh + 2 * BoxPoints) { dSendHigh = UpWick; WipeUpWick = true; } else { dSendHigh = PrevHigh; } dSendClose = PrevClose; dSendVol = CurVolume; DoWriteStruct(dtSendTime, dSendOpen, dSendHigh, dSendLow, dSendClose, dSendVol); if(WipeUpWick) UpWick = 0; if(WipeDownWick) DnWick = EMPTY_VALUE; CurVolume = 0; CurHigh = PrevLow; CurLow = PrevLow; if(PrevTime < Time[i]) PrevTime = Time[i]; else PrevTime++; } // go up while(High[i] > PrevHigh + BoxPoints || CompareDoubles(High[i], PrevHigh + BoxPoints)) { PrevHigh = PrevHigh + BoxPoints; PrevLow = PrevLow + BoxPoints; PrevOpen = PrevLow; PrevClose = PrevHigh; dtSendTime = PrevTime; dSendOpen = PrevOpen; if(ShowWicks==true && DnWick < PrevLow && DnWick >= PrevLow - 2 * BoxPoints ) { dSendLow = DnWick; WipeDownWick = true; } else { dSendLow = PrevLow; } if(ShowWicks==true && UpWick > PrevHigh && High[i] <= PrevHigh + BoxPoints) { dSendHigh = UpWick; WipeUpWick = true; } else { //dSendHigh = PrevHigh; // When ShowWicks = false this code will show wicks (file45) dSendHigh = PrevLow; } dSendClose = PrevClose; dSendVol = CurVolume; DoWriteStruct(dtSendTime, dSendOpen, dSendHigh, dSendLow, dSendClose, dSendVol); if(WipeUpWick) UpWick = 0; if(WipeDownWick) DnWick = EMPTY_VALUE; CurVolume = 0; CurHigh = PrevHigh; CurLow = PrevHigh; if(PrevTime < Time[i]) PrevTime = Time[i]; else PrevTime++; RefreshRates(); } // go down in down phase while(!UpTrend && (Low[i] < PrevLow - BoxPoints || CompareDoubles(Low[i], PrevLow - BoxPoints))) { PrevHigh = PrevHigh - BoxPoints; PrevLow = PrevLow - BoxPoints; PrevOpen = PrevHigh; PrevClose = PrevLow; dtSendTime = PrevTime; dSendOpen = PrevOpen; if(ShowWicks==true && DnWick < PrevLow && Low[i] >= PrevLow - BoxPoints) { dSendLow = DnWick; WipeDownWick = true; } else { //dSendLow = PrevLow; // When ShowWicks = false this code will show wicks (file45) dSendLow = PrevHigh; } if(ShowWicks==true && UpWick > PrevHigh) { dSendHigh = UpWick; WipeUpWick = true; } else { dSendHigh = PrevHigh; } dSendClose = PrevClose; dSendVol = CurVolume; DoWriteStruct(dtSendTime, dSendOpen, dSendHigh, dSendLow, dSendClose, dSendVol); if(WipeUpWick) UpWick = 0; if(WipeDownWick) DnWick = EMPTY_VALUE; CurVolume = 0; CurHigh = PrevLow; CurLow = PrevLow; if(PrevTime < Time[i]) PrevTime = Time[i]; else PrevTime++; RefreshRates(); } i--; } LastFPos = (int)FileTell(HstHandle); // Remember Last pos in file if(Close[0] > MathMax(PrevClose, PrevOpen)) CurOpen = MathMax(PrevClose, PrevOpen); else if(Close[0] < MathMin(PrevClose, PrevOpen)) CurOpen = MathMin(PrevClose, PrevOpen); else CurOpen = Close[0]; CurClose = Close[0]; if(ShowWicks==true) { if(UpWick > PrevHigh) CurHigh = UpWick; if(DnWick < PrevLow) CurLow = DnWick; } dtSendTime = PrevTime; dSendOpen = CurOpen; dSendLow = CurLow; dSendHigh = CurHigh; dSendClose = CurClose; dSendVol = CurVolume; DoWriteStruct(dtSendTime, dSendOpen, dSendHigh, dSendLow, dSendClose, dSendVol); FileFlush(HstHandle); if(bStopAll) return(0); Comment("RenkoLiveChart (" + DoubleToString(RenkoBoxSize) + "): Open Offline ", sSymbolName, ",M", RenkoTimeFrame, " To View Chart");//3 UpdateChartWindow(); return(0); // End historical data / Init } //---------------------------------------------------------------------------- // HstHandle not < 0 so we always enter here after history done // Begin live data feed if(bStopAll) return(0); UpWick = MathMax(UpWick, Bid); DnWick = MathMin(DnWick, Bid); CurVolume++; FileSeek(HstHandle, LastFPos, SEEK_SET); //------------------------------------------------------------------------- // up box if(Bid > PrevHigh + BoxPoints || CompareDoubles(Bid, PrevHigh + BoxPoints)) { PrevHigh = PrevHigh + BoxPoints; PrevLow = PrevLow + BoxPoints; PrevOpen = PrevLow; PrevClose = PrevHigh; dtSendTime = PrevTime; dSendOpen = PrevOpen; if(ShowWicks==true && DnWick < PrevLow) { dSendLow = DnWick; } else { dSendLow = PrevLow; } if(ShowWicks==true && UpWick > PrevHigh) { dSendHigh = UpWick; } else { dSendHigh = PrevHigh; } dSendClose = PrevClose; dSendVol = CurVolume; DoWriteStruct(dtSendTime, dSendOpen, dSendHigh, dSendLow, dSendClose, dSendVol); FileFlush(HstHandle); LastFPos = (int) FileTell(HstHandle); // Remeber Last pos in file if(PrevTime < TimeCurrent()) PrevTime = TimeCurrent(); else PrevTime++; CurVolume = 0; CurHigh = PrevHigh; CurLow = PrevHigh; UpWick = 0; DnWick = EMPTY_VALUE; UpdateChartWindow(); } //------------------------------------------------------------------------- // down box else if(Bid < PrevLow - BoxPoints || CompareDoubles(Bid, PrevLow - BoxPoints)) { PrevHigh = PrevHigh - BoxPoints; PrevLow = PrevLow - BoxPoints; PrevOpen = PrevHigh; PrevClose = PrevLow; dtSendTime = PrevTime; dSendOpen = PrevOpen; if(ShowWicks==true && DnWick < PrevLow) { dSendLow = DnWick; } else { dSendLow = PrevLow; } if(ShowWicks==true && UpWick > PrevHigh) { dSendHigh = UpWick; } else { dSendHigh = PrevHigh; } dSendClose = PrevClose; dSendVol = CurVolume; DoWriteStruct(dtSendTime, dSendOpen, dSendHigh, dSendLow, dSendClose, dSendVol); FileFlush(HstHandle); LastFPos = (int)FileTell(HstHandle); // Remeber Last pos in file if(PrevTime < TimeCurrent()) PrevTime = TimeCurrent(); else PrevTime++; CurVolume = 0; CurHigh = PrevLow; CurLow = PrevLow; UpWick = 0; DnWick = EMPTY_VALUE; UpdateChartWindow(); } //------------------------------------------------------------------------- // no box - high/low not hit else { if(Bid > CurHigh) CurHigh = Bid; if(Bid < CurLow) CurLow = Bid; if(PrevHigh <= Bid) CurOpen = PrevHigh; else if(PrevLow >= Bid) CurOpen = PrevLow; else CurOpen = Bid; CurClose = Bid; dtSendTime = PrevTime; dSendOpen = CurOpen; dSendLow = CurLow; dSendHigh = CurHigh; dSendClose = CurClose; dSendVol = CurVolume; DoWriteStruct(dtSendTime, dSendOpen, dSendHigh, dSendLow, dSendClose, dSendVol, false); FileFlush(HstHandle); UpdateChartWindow(); } return(rates_total); } void UpdateChartWindow() { if(hwnd == 0) { hwnd = WindowHandle(sSymbolName, RenkoTimeFrame); if(hwnd != 0) Print("Chart window detected"); } if(EmulateOnLineChart && MT4InternalMsg == 0) MT4InternalMsg = RegisterWindowMessageW("MetaTrader4_Internal_Message"); if(hwnd != 0) if(PostMessageW(hwnd, WM_COMMAND, 0x822c, 0) == 0) hwnd = 0; if(hwnd != 0 && MT4InternalMsg != 0) PostMessageW(hwnd, MT4InternalMsg, 2, 1); return; } void DoWriteStruct(datetime dtTime, double dOpen, double dHigh, double dLow, double dClose, double dVol, bool history = true) { iRcdCnt++; static int iErr = 0; static int iBWritn = 0; MqlRates rate; rate.time = dtTime; rate.open = dOpen; static double prevHigh = 0, prevLow = DBL_MAX; if(history) { rate.high = (dOpen < dClose)?dClose:MathMax(dHigh, prevHigh); rate.low = (dOpen > dClose)?dClose:MathMin(dLow, prevLow); prevHigh = (dOpen < dClose)?dHigh:0; prevLow = (dOpen > dClose)?dLow:DBL_MAX; } else { rate.high = dHigh; rate.low = dLow; } rate.close = dClose; rate.tick_volume = (long) dVol; rate.spread = 0; rate.real_volume = (long) dVol; iBWritn = (int)FileWriteStruct(HstHandle, rate); if(iBWritn == 0) { iErr = GetLastError(); Print("Error on write struct at cntr: " + IntegerToString(iRcdCnt) + ", errdesc: " + ErrorDescription(iErr));//W4 bStopAll = true; } return; } void DoOpenHistoryReadWrite() { if(HstHandle >= 0) { int iSz = (int)FileSize(HstHandle); Print("fl sz " + IntegerToString(iSz)); FileClose(HstHandle); HstHandle = -1; } HstHandle = FileOpenHistory( sSymbolName + IntegerToString(RenkoTimeFrame) + ".hst", FILE_BIN | FILE_READ | FILE_WRITE | FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_ANSI); // this combination is critical to unlock the file and still allow read write and opening a chart on it if(HstHandle < 0) { Print("Error: cant open history file read write: " + ErrorDescription(GetLastError()) + ": " + sSymbolName + IntegerToString(RenkoTimeFrame) + ".hst"); bStopAll = true; } else { Print("Hist file opened for read write, handle: " + IntegerToString(HstHandle)); } return; }