/////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 Indicator // --------------------------------------------------- // Name: X121 XReplay // Description: SaherElm Replay Tool ... // // // Maintainer: // ------------ // Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) // ////////////////////////////////////////////////////// // // Global Properties ... #property copyright "Copyright 2023, SaherElm IT Center" #property link "https://saherelm.ir" #property version "1.00" #property description "SaherElm X121 XReplay Indicator" #property icon "../Images/SaherElm_Logo_Color.ico" #property strict // #region Imports ... #include "../Libraries/x-saherelm.common.lib.mq5" // #endregion // #region Definitions ... // #define ShortName "X121-XReplay" // // Global Variable Keys for TimeFrame Change ... #define GV_ACTIVE_SUFFIX "_Active" #define GV_TIME_SUFFIX "_LastVisibleTime" #define GV_VISIBLE_BARS_SUFFIX "_VisibleBars" // #define hideColorIDX 0 #define bullishColorIDX 1 #define bearishColorIDX 2 #define neuturalColorIDX 3 // #define bullishState 1 #define neuturalState 0 #define bearishState -1 // #define emptyValue 0.0 // // an strucutre to save Chart Style ... struct XChartStyle { long chartId; // chart's ID ... ENUM_CHART_MODE mode; // chart's mode ... bool showBidLine; // show bid line ... bool showAskLine; // show ask line ... bool showGrid; // show grids on chart ... bool showVolumes; // show volumes ... bool showTradeLevels; // Displaying trade levels in the chart (levels of open positions, Stop Loss, Take Profit and pending orders) ... bool autoScroll; // chart autoscroll ... bool quickNavigation; // chart quick navigation state ... color foreGroundColor; // chart's foreground color ... color backGroundColor; // chart's background color ... color upColor; // Up Color ... color downColor; // Down Color ... color bullishColor; // Bullish color ... color bearishColor; // Bearish color ... color gridColor; // grid color ... color bidLineColor; // bid line color ... color askLineColor; // ask line color ... color lineColor; // line mode and doji candlestick color ... color stopColor; // Color of stop order levels (Stop Loss and Take Profit) ... color volumesColor; // volumes color ... }; // #endregion // #region Inputs ... input group "Replay Settings"; input int startBar = 20; // Cutof Bars input int autoPlaySpeed = 1000; // Auto-Play Speed (ms) input group "Presentation"; input color panelButtonTextColor = clrYellow; // Button Text Color input color panelBackgroundColor = clrBlack; // Panel Background input color infoColor = clrWhite; // Info Color input color infoPanelBorderColor = clrBlack; // Info Panel Border Color // #endregion // #region Buffers ... #define buffersCount 0 #property indicator_chart_window #property indicator_buffers 5 #property indicator_plots 1 // // Played Bar ... // #define pBarOpenBufferIndex buffersCount double pBarOpenBuffer[]; // #define pBarHighBufferIndex buffersCount + 1 double pBarHighBuffer[]; // #define pBarLowBufferIndex buffersCount + 2 double pBarLowBuffer[]; // #define pBarCloseBufferIndex buffersCount + 3 double pBarCloseBuffer[]; // #define pBarColorBufferIndex buffersCount + 4 double pBarColorBuffer[]; // #define pBarBufferPlotIndex 0 #property indicator_label1 "XPBar Open;XPBar High;XPBar Low;XPBar Close" #property indicator_type1 DRAW_COLOR_CANDLES #property indicator_color1 CLR_NONE, clrDodgerBlue, clrDarkOrange // #endregion // #region Variables ... // int xReplyLimit; int xReplyMaxLength; int xReplyFirstBarIndex; // XChartStyle xReplyChartStyle; XChartStyle xReplyClearStyle; string xReplyChartStyleFileName = "XChartStyle_" + ShortName + "_" + _Symbol + "_" + ToXString(_Period) + ".bin"; // // UI Object Names ... string xReplyBtnPlay; string xReplyBtnPrev; string xReplyBtnNext; string xReplyLblInfo; string xReplyBtnPause; string xReplyBtnReset; // // Replay State ... int xReplyCandlesCount = 0; int xReplyStartBarIndex = 0; int xReplyCurrentBarIndex = 0; bool xReplyIsPlaying = false; ulong xReplyLastPlayTick = 0; datetime xReplyStartTime = NULL; int xReplyCurrentVisibleBars = 0; // bool xReplyTimerActive = false; // Timer is active only when playing int xReplyLastDrawnBarCount = 0; // Track last drawn bar count for incremental draw bool xReplyIsResumedFromTFChange = false; // Flag: resumed from timeframe change // #endregion // #region Event Handlers ... /** * Initialize Indicator ... * * @return ( int ) */ int OnInit() { // bool has = false; // // Validate Inputs ... has = ValidateInputs(); if (!has) { return INIT_PARAMETERS_INCORRECT; } // // because in some cases we may have more than one input for // calculation and we must prevent any calculation // untill we pass the biggest input length, here we get max Input length // and then wait until pass it ... xReplyMaxLength = ExtractxReplyMaxLengthOfInputs(); // // Initial Requirements ... has = InitRequirements(); if (!has) { return INIT_FAILED; } // // Define Index Buffers ... DefineBuffers(); // // Set Indicator ShortName ... SetIndicatorName(); // // Apply Chart Style ... ApplyCustomChartStyle(); // xReplyIsResumedFromTFChange = TryResumeFromTimeFrameChange(); if (!xReplyIsResumedFromTFChange) { // int idx = startBar; if (idx >= Bars(_Symbol, _Period)) { idx = Bars(_Symbol, _Period) - 10; } // xReplyStartTime = iTime(_Symbol, _Period, idx); xReplyStartBarIndex = iBarShift(_Symbol, _Period, xReplyStartTime, false); has = xReplyStartBarIndex >= 0; if (!has) { return INIT_PARAMETERS_INCORRECT; } // xReplyCurrentVisibleBars = 1; xReplyCurrentBarIndex = xReplyStartBarIndex; } else { // // Validate Resumed State ... if (xReplyStartBarIndex < 0) { return INIT_PARAMETERS_INCORRECT; } } // // Create UI Panel ... CreatePanel(); // // Initial Draw ... DrawVisibleCandles(); UpdateInfoLabel(); // // Init Succeed ... return INIT_SUCCEEDED; // } /** * De Initialize Indicator ... * * @param reason: Integer, De Initialization Reason ... * - REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function * - REASON_REMOVE 1 Program removed from a chart * - REASON_RECOMPILE 2 Program recompiled * - REASON_CHARTCHANGE 3 A symbol or a chart period is changed * - REASON_CHARTCLOSE 4 Chart closed * - REASON_PARAMETERS 5 Inputs changed by a user * - REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings * - REASON_TEMPLATE 7 Another chart template applied * - REASON_INITFAILED 8 The OnInit() handler returned a non-zero value * - REASON_CLOSE 9 Terminal closed */ void OnDeinit(const int reason) { if (reason == REASON_CHARTCHANGE) { // // Save Global Variables ... SaveReplayStateForTimeFrameChange(); } else { // // Remove Global Variables for Other Reasons ... RemoveReplayGlobalVariables(); } // // Kill Timer if it's Activated ... if (xReplyTimerActive) { StopTimer(); } // // Show Saved Chart Style ... ShowBars(); // // Remove Chart Style File ... FileDelete(xReplyChartStyleFileName); // // Remove UI Objects ... RemoveUIObjects(); } /** * Calculate Bars ... * * @param rates_total: Integer, Total Bars on Chart ... * @param prev_calculated: Integer, Total Calculated Bars on Charts ... * @param time: DateTime Array, History of Open Time ... * @param open: Double Array, History of Open Prices ... * @param high: Double Array, History of High Prices ... * @param low: Double Array, History of Low Prices ... * @param close: Double Array, History of Close Prices ... * @param tick_volume: Long, History of Tick Volumes on Bar ... * @param volume: Long, History of Trade Volumes ... * @param spread: Double, History of Spread Price ... * * @return ( int ) */ 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[] // ) { // // Prepare Buffers ... ArraySetAsSeries(time, true); ArraySetAsSeries(open, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); ArraySetAsSeries(tick_volume, true); ArraySetAsSeries(volume, true); ArraySetAsSeries(spread, true); // // Calculate Size of Loop ... xReplyLimit = (prev_calculated > rates_total || prev_calculated <= 0) // // First Call ... ? rates_total - 1 // // Subsequent calls: only new bars + current forming bar ... : (rates_total - prev_calculated) + 1; // // Main Loop ... for (int i = xReplyLimit - 1; i >= 0 && !IsStopped(); i--) { // CalculateBuffers( i, prev_calculated, rates_total, // open, high, close, low, tick_volume // ); } // // Incremental color update ... UpdateVisibleColors(); // return rates_total; } /** * Handle Timer Event ... */ void OnTimer() { // if (!xReplyIsPlaying) { // // Stop Timer ... if (xReplyTimerActive) { StopTimer(); } return; } // ulong now = GetTickCount64(); if (now - xReplyLastPlayTick >= (ulong)autoPlaySpeed) { // xReplyLastPlayTick = now; StepForward(); // // Auto Stop at Current Market Bar ... if (xReplyCurrentBarIndex <= 0) { // xReplyIsPlaying = false; ObjectSetInteger(0, xReplyBtnPlay, OBJPROP_STATE, false); ObjectSetInteger(0, xReplyBtnPause, OBJPROP_STATE, true); } } } /** * Handle Chart Events ... * * @param id: int, Event id ... * @param lparam: long, Event Long Parameter ... * @param dparam: double, Event Double Parameter ... * @param sparam: string, Event String Parameter ... */ void OnChartEvent( const int id, // Event id ... const long &lparam, // Event Long Parameter ... const double &dparam, // Event Double Parameter ... const string &sparam // Event String Parameter ... ) { // // Button Clicks ... if (id == CHARTEVENT_OBJECT_CLICK) { // if (sparam == xReplyBtnNext) { // StepForward(); ObjectSetInteger(0, xReplyBtnNext, OBJPROP_STATE, false); ChartRedraw(); } else if (sparam == xReplyBtnPrev) { // StepBackward(); ObjectSetInteger(0, xReplyBtnPrev, OBJPROP_STATE, false); ChartRedraw(); } else if (sparam == xReplyBtnPlay) { // xReplyIsPlaying = true; ObjectSetInteger(0, xReplyBtnPlay, OBJPROP_STATE, true); ObjectSetInteger(0, xReplyBtnPause, OBJPROP_STATE, false); ChartRedraw(); } else if (sparam == xReplyBtnPause) { // xReplyIsPlaying = false; ObjectSetInteger(0, xReplyBtnPause, OBJPROP_STATE, true); ObjectSetInteger(0, xReplyBtnPlay, OBJPROP_STATE, false); ChartRedraw(); } else if (sparam == xReplyBtnReset) { // ResetReplay(); ObjectSetInteger(0, xReplyBtnReset, OBJPROP_STATE, false); ChartRedraw(); } // return; } // // Keyboard Shortcuts ... if (id == CHARTEVENT_KEYDOWN) { // switch ((int)lparam) { // // Arrow Right ... case 38: case 39: StepForward(); break; // // Arrow Left ... case 37: case 40: StepBackward(); break; // // Space ... case 32: xReplyIsPlaying = !xReplyIsPlaying; ObjectSetInteger(0, xReplyBtnPlay, OBJPROP_STATE, xReplyIsPlaying); ObjectSetInteger(0, xReplyBtnPause, OBJPROP_STATE, !xReplyIsPlaying); ChartRedraw(); break; // // R ... case 82: ResetReplay(); break; } // return; } } // #endregion // #region Custm Functions ... /** * Set Indicator Short Name and also we can define Buffers Labels ... */ void SetIndicatorName() { // IndicatorSetInteger(INDICATOR_DIGITS, _Digits); IndicatorSetString(INDICATOR_SHORTNAME, ShortName); } /** * Validate Input Args for Initialization ... * * @return ( bool ) */ bool ValidateInputs() { // bool result = false; // result = // startBar > 0 // ; // return result; } /** * Extract Max Length of Inputs ... * * @return ( int ) */ int ExtractxReplyMaxLengthOfInputs() { // int result = 0; // // return result; } /** * Initial all Indicator Requirements ... * * @return ( false ) * */ bool InitRequirements() { // bool result = false; // // Handlers ... result = true; // return result; } /** * Define Required Buffers ... */ void DefineBuffers() { // #region Plot Buffers ... // // SIGNAL Bar ... // // Signal Bars Color ... ArraySetAsSeries(pBarColorBuffer, true); SetIndexBuffer(pBarColorBufferIndex, pBarColorBuffer, INDICATOR_COLOR_INDEX); // // Set EMPTY_VALUE on XHK Bar Buffer ... PlotIndexSetDouble(pBarBufferPlotIndex, PLOT_EMPTY_VALUE, emptyValue); PlotIndexSetInteger(pBarBufferPlotIndex, PLOT_SHOW_DATA, false); // // Open ... ArraySetAsSeries(pBarOpenBuffer, true); SetIndexBuffer(pBarOpenBufferIndex, pBarOpenBuffer, INDICATOR_DATA); // // High ... ArraySetAsSeries(pBarHighBuffer, true); SetIndexBuffer(pBarHighBufferIndex, pBarHighBuffer, INDICATOR_DATA); // // Low ... ArraySetAsSeries(pBarLowBuffer, true); SetIndexBuffer(pBarLowBufferIndex, pBarLowBuffer, INDICATOR_DATA); // // Close ... ArraySetAsSeries(pBarCloseBuffer, true); SetIndexBuffer(pBarCloseBufferIndex, pBarCloseBuffer, INDICATOR_DATA); // #endregion } /** * Calculate Custom Buffers ... * * @param barIndex: Integer, Represent Current Bar ... * @param prevCalculated: Integer, Represent Previous Calculated Bars ... * @param ratesTotal: Integer, Represents All Available Bars ... * @param open: Double Array, History of Open Prices ... * @param high: Double Array, History of High Prices ... * @param close: Double Array, History of Close Prices ... * @param low: Double Array, History of Low Prices ... * @param tickVolume: Long, History of Tick Volumes on Bar ... */ void CalculateBuffers( int barIndex, const int prevCalculated, const int ratesTotal, const double &open[], const double &high[], const double &close[], const double &low[], const long &tickVolume[] // ) { // int barsxReplyLimit = ratesTotal; xReplyFirstBarIndex = barsxReplyLimit - 1; // bool canCalculate = barIndex <= barsxReplyLimit; if (canCalculate) { // // Calculate Values ... CalculateValues( barIndex, prevCalculated, ratesTotal, open, high, close, low, tickVolume // ); } else { FillBuffersZero(barIndex); } } /** * Fill All Bufers to Zero Vlue for Specified Bar Index ... * * @param barIndex: Integer ... */ void FillBuffersZero(int barIndex) { pBarColorBuffer[barIndex] = hideColorIDX; } /** * Start Timer only when needed ... */ void StartTimer() { // if (!xReplyTimerActive) { EventSetMillisecondTimer(100); xReplyTimerActive = true; } } /** * Stop Timer to save resources ... */ void StopTimer() { // if (xReplyTimerActive) { EventKillTimer(); xReplyTimerActive = false; } } // #endregion // #region Value Calculators ... /** * Calculate Values ... * * @param barIndex: int, Specified Bar Index ... * @param prevCalculated: int, Provides Previous Calculated Bars ... * @param ratesTotal: int, Provides All Availabled Bars ... * @param open: double Collection, Provides Open Prices Time Series ... * @param high: double Collection, Provides High Prices Time Series ... * @param close: double Collection, Provides Close Prices Time Series ... * @param low: double Collection, Provides Low Prices Time Series ... * @param tickVolume: long Collection, Provides Ticks Volume Prices Time Series ... */ void CalculateValues( int barIndex, const int prevCalculated, const int ratesTotal, const double &open[], const double &high[], const double &close[], const double &low[], const long &tickVolume[] // ) { // int lastBarIndex = barIndex + 1; int maxBarIndex = ratesTotal; bool isFirstBar = barIndex == xReplyFirstBarIndex; // // Filling Buffers ... pBarLowBuffer[barIndex] = low[barIndex]; pBarOpenBuffer[barIndex] = open[barIndex]; pBarHighBuffer[barIndex] = high[barIndex]; pBarCloseBuffer[barIndex] = close[barIndex]; // // Actually Bars Color Index ... int colorIDX = close[barIndex] >= open[barIndex] ? bullishColorIDX : close[barIndex] < open[barIndex] ? bearishColorIDX : neuturalColorIDX; // int selectedColorIDX = colorIDX; if (barIndex <= startBar) { selectedColorIDX = hideColorIDX; } pBarColorBuffer[barIndex] = selectedColorIDX; } // #endregion // #region Chart Style ... /** * Show Bars ... */ void ShowBars() { LoadChartStyleFromFile(xReplyChartStyle); SetChartStyle(xReplyChartStyle); } /** * Show Hide ... */ void HideBars() { SetChartStyle(xReplyClearStyle); } /** * Read and Save Chart Style ... */ void ReadChartStyle() { // if (LoadChartStyleFromFile(xReplyChartStyle)) { return; } // // Retrieve Current Chart ID ... long chartId = ChartID(); xReplyChartStyle.chartId = chartId; // // Retrieve Chart Mode ... xReplyChartStyle.mode = (ENUM_CHART_MODE)ChartGetInteger(chartId, CHART_MODE); // xReplyChartStyle.showBidLine = (bool)ChartGetInteger(chartId, CHART_SHOW_BID_LINE); xReplyChartStyle.showAskLine = (bool)ChartGetInteger(chartId, CHART_SHOW_ASK_LINE); xReplyChartStyle.showGrid = (bool)ChartGetInteger(chartId, CHART_SHOW_GRID); xReplyChartStyle.showVolumes = (bool)ChartGetInteger(chartId, CHART_SHOW_VOLUMES); xReplyChartStyle.showTradeLevels = (bool)ChartGetInteger(chartId, CHART_SHOW_TRADE_LEVELS); xReplyChartStyle.autoScroll = (bool)ChartGetInteger(chartId, CHART_AUTOSCROLL); xReplyChartStyle.quickNavigation = (bool)ChartGetInteger(chartId, CHART_QUICK_NAVIGATION); // xReplyChartStyle.foreGroundColor = (color)ChartGetInteger(chartId, CHART_COLOR_FOREGROUND); xReplyChartStyle.backGroundColor = (color)ChartGetInteger(chartId, CHART_COLOR_BACKGROUND); xReplyChartStyle.upColor = (color)ChartGetInteger(chartId, CHART_COLOR_CHART_UP); xReplyChartStyle.downColor = (color)ChartGetInteger(chartId, CHART_COLOR_CHART_DOWN); xReplyChartStyle.bullishColor = (color)ChartGetInteger(chartId, CHART_COLOR_CANDLE_BULL); xReplyChartStyle.bearishColor = (color)ChartGetInteger(chartId, CHART_COLOR_CANDLE_BEAR); xReplyChartStyle.gridColor = (color)ChartGetInteger(chartId, CHART_COLOR_GRID); xReplyChartStyle.bidLineColor = (color)ChartGetInteger(chartId, CHART_COLOR_BID); xReplyChartStyle.askLineColor = (color)ChartGetInteger(chartId, CHART_COLOR_ASK); xReplyChartStyle.stopColor = (color)ChartGetInteger(chartId, CHART_COLOR_STOP_LEVEL); xReplyChartStyle.lineColor = (color)ChartGetInteger(chartId, CHART_COLOR_CHART_LINE); xReplyChartStyle.volumesColor = (color)ChartGetInteger(chartId, CHART_COLOR_VOLUME); // SaveChartStyleToFile(xReplyChartStyle); } /** * Setting Chart Style based on given Configuration ... * * @param mChartStyle: XChartStyle instance ... */ void SetChartStyle( XChartStyle &mChartStyle // Chart Style to Apply ) { // ApplyChartStyle( mChartStyle.chartId, mChartStyle.mode, mChartStyle.showBidLine, mChartStyle.showAskLine, mChartStyle.showGrid, mChartStyle.showVolumes, mChartStyle.showTradeLevels, mChartStyle.autoScroll, mChartStyle.quickNavigation, mChartStyle.foreGroundColor, mChartStyle.backGroundColor, mChartStyle.upColor, mChartStyle.downColor, mChartStyle.bullishColor, mChartStyle.bearishColor, mChartStyle.gridColor, mChartStyle.bidLineColor, mChartStyle.askLineColor, mChartStyle.lineColor, mChartStyle.stopColor, mChartStyle.volumesColor); } /** * Setting Chart Style by Providing required data ... * * @param mChartId: int, Chart ID ... * @param mMode: ENUM_CHART_MODE, Chart Bars Style ... * @param mShowBidLine: bool, Show Bid Line ... * @param mShowAskLine: bool, Show Ask Line ... * @param mShowGrid: bool, Show Grids ... * @param mShowVolumes: bool, Show Volumes ... * @param mShowTradeLevels: bool, Show Trade Levels ... * @param mAutoScroll: bool, Auto Scroll Chart ... * @param mQuickNavigation: bool, Show Quick Navigations ... * @param mForeGroundColor: color, Foreground Color ... * @param mBackGroundColor: color, Background Color ... * @param mUpColor: color, Up Bars Color ... * @param mDownColor: color, Down Bars Color ... * @param mBullishColor: color, Bullish Bars Color ... * @param mBearishColor: color, Bearish Bars Color ... * @param mGridColor: color, Chart Grid Color ... * @param mBidLineColor: color, Bid Line Color ... * @param mAskLineColor: color, Ask Line Color ... * @param mLineColor: color, Bars Line Color ... * @param mStopColor: color, Trading Stop Color ... * @param mVolumesColor: color, Volumes Bar Color ... */ void ApplyChartStyle( long mChartId = -1, // chart's ID ENUM_CHART_MODE mMode = CHART_CANDLES, // chart's mMode bool mShowBidLine = true, // show bid line bool mShowAskLine = true, // show ask line bool mShowGrid = false, // show grids on chart bool mShowVolumes = false, // show volumes bool mShowTradeLevels = true, // Displaying trade levels in the chart (levels of open positions, Stop Loss, Take Profit and pending orders) bool mAutoScroll = true, // chart autoscroll bool mQuickNavigation = true, // chart quick navigation state color mForeGroundColor = clrWhite, // chart's foreground color color mBackGroundColor = clrBlack, // chart's background color color mUpColor = clrGreen, // Up Color color mDownColor = clrRed, // Down Color color mBullishColor = clrGreen, // Bullish color color mBearishColor = clrRed, // Bearish color color mGridColor = clrGray, // grid color color mBidLineColor = clrGray, // bid line color color mAskLineColor = clrRed, // ask line color color mLineColor = clrLime, // line mMode and doji candlestick color color mStopColor = clrGold, // Color of stop order levels (Stop Loss and Take Profit) color mVolumesColor = clrGreen // volumes color ) { // // Validate Args ... if (mChartId == -1) { mChartId = 0; } // ChartSetInteger(mChartId, CHART_MODE, mMode); ChartSetInteger(mChartId, CHART_SHOW_BID_LINE, mShowBidLine); ChartSetInteger(mChartId, CHART_SHOW_ASK_LINE, mShowAskLine); ChartSetInteger(mChartId, CHART_SHOW_GRID, mShowGrid); ChartSetInteger(mChartId, CHART_SHOW_VOLUMES, mShowVolumes); ChartSetInteger(mChartId, CHART_SHOW_TRADE_LEVELS, mShowTradeLevels); ChartSetInteger(mChartId, CHART_AUTOSCROLL, mAutoScroll); ChartSetInteger(mChartId, CHART_QUICK_NAVIGATION, mQuickNavigation); ChartSetInteger(mChartId, CHART_COLOR_FOREGROUND, mForeGroundColor); ChartSetInteger(mChartId, CHART_COLOR_BACKGROUND, mBackGroundColor); ChartSetInteger(mChartId, CHART_COLOR_CHART_UP, mUpColor); ChartSetInteger(mChartId, CHART_COLOR_CHART_DOWN, mDownColor); ChartSetInteger(mChartId, CHART_COLOR_CANDLE_BULL, mBullishColor); ChartSetInteger(mChartId, CHART_COLOR_CANDLE_BEAR, mBearishColor); ChartSetInteger(mChartId, CHART_COLOR_GRID, mGridColor); ChartSetInteger(mChartId, CHART_COLOR_BID, mBidLineColor); ChartSetInteger(mChartId, CHART_COLOR_ASK, mAskLineColor); ChartSetInteger(mChartId, CHART_COLOR_STOP_LEVEL, mStopColor); ChartSetInteger(mChartId, CHART_COLOR_CHART_LINE, mLineColor); ChartSetInteger(mChartId, CHART_COLOR_VOLUME, mVolumesColor); // ChartRedraw(); } /** * Apply Custom Chart Style ... */ void ApplyCustomChartStyle() { // // Read Current Chart Config and Store it ... ReadChartStyle(); // // After Reading Current Chart Style ... // we have to Save Configs and Change Styles ... xReplyClearStyle = xReplyChartStyle; // xReplyClearStyle.upColor = CLR_NONE; xReplyClearStyle.downColor = CLR_NONE; xReplyClearStyle.lineColor = CLR_NONE; xReplyClearStyle.bullishColor = CLR_NONE; xReplyClearStyle.bearishColor = CLR_NONE; // // Decide to Show or not Candles based on User Input ... HideBars(); } /** * Save Chart Style ... * * @param style: XChartStyle instance ... * * @return ( bool ) */ bool SaveChartStyleToFile( const XChartStyle &style // ) { // bool result = false; // // Create a File Handler for Writing Content to File ... int fileHandler = FileOpen(xReplyChartStyleFileName, FILE_WRITE | FILE_BIN | FILE_COMMON); result = fileHandler != INVALID_HANDLE; if (!result) { return result; } // // Writing Struct to File ... uint bytesWritten = FileWriteStruct(fileHandler, style); FileClose(fileHandler); result = bytesWritten > 0; // return result; } /** * Load Chart Style ... * * @param style: XChartStyle instance ... * * @return ( bool ) */ bool LoadChartStyleFromFile( XChartStyle &style // ) { // bool result = false; // // Create a File Handler for Reading File Content ... int fileHandler = FileOpen(xReplyChartStyleFileName, FILE_READ | FILE_BIN | FILE_COMMON); result = fileHandler != INVALID_HANDLE; if (!result) { return result; } // // Read File Content and Fill Struct ... uint bytesRead = FileReadStruct(fileHandler, style); FileClose(fileHandler); // result = bytesRead > 0; if (!result) { return result; } // return result; } // #endregion // #region UI ... /** * Create UI Panel ... */ void CreatePanel() { // xReplyBtnPlay = ShortName + "_BTN_PLAY"; xReplyBtnPause = ShortName + "_BTN_PAUSE"; xReplyBtnPrev = ShortName + "_BTN_PREV"; xReplyBtnNext = ShortName + "_BTN_NEXT"; xReplyBtnReset = ShortName + "_BTN_RESET"; xReplyLblInfo = ShortName + "_LBL_INFO"; // int x = 20; int y = 30; int w = 45; int h = 30; int gap = 5; // // Prev Button ... CreateButton( xReplyBtnPrev, x, y, w, h, "|<", clrLightSkyBlue // ); // // Play Button ... CreateButton( xReplyBtnPlay, x + (w + gap) * 1, y, w, h, ">", clrLimeGreen // ); // // Pause Button ... CreateButton( xReplyBtnPause, x + (w + gap) * 2, y, w, h, "||", clrGoldenrod // ); // // Next Button ... CreateButton( xReplyBtnNext, x + (w + gap) * 3, y, w, h, ">|", clrLightSkyBlue // ); // // Reset Button ... CreateButton( xReplyBtnReset, x + (w + gap) * 4, y, w, h, "R", clrTomato // ); // // Info Label ... ObjectCreate(0, xReplyLblInfo, OBJ_EDIT, 0, 0, 0); ObjectSetInteger(0, xReplyLblInfo, OBJPROP_XDISTANCE, x); ObjectSetInteger(0, xReplyLblInfo, OBJPROP_YDISTANCE, y + h + gap); ObjectSetInteger(0, xReplyLblInfo, OBJPROP_XSIZE, (w + gap) * 5 - gap); ObjectSetInteger(0, xReplyLblInfo, OBJPROP_YSIZE, 22); ObjectSetInteger(0, xReplyLblInfo, OBJPROP_BGCOLOR, panelBackgroundColor); ObjectSetInteger(0, xReplyLblInfo, OBJPROP_COLOR, infoColor); ObjectSetInteger(0, xReplyLblInfo, OBJPROP_BORDER_COLOR, infoPanelBorderColor); ObjectSetInteger(0, xReplyLblInfo, OBJPROP_READONLY, true); ObjectSetInteger(0, xReplyLblInfo, OBJPROP_FONTSIZE, 9); ObjectSetString(0, xReplyLblInfo, OBJPROP_FONT, "Arial"); ObjectSetInteger(0, xReplyLblInfo, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, xReplyLblInfo, OBJPROP_SELECTABLE, false); // ChartRedraw(); } /** * Create Specified Button ... * * @param name: string, Object Name ... * @param x: int, X Position ... * @param y: int, Y Position ... * @param w: int, Width ... * @param h: int, Height ... * @param text: int Button Text ... * @param borderClr: color, Border Color ... */ void CreateButton( string name, // Button Name ... int x, // X Position ... int y, // Y Position ... int w, // Width ... int h, // Height ... string text, // Button Text ... color borderClr // Border Color ... ) { // ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x); ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); ObjectSetInteger(0, name, OBJPROP_XSIZE, w); ObjectSetInteger(0, name, OBJPROP_YSIZE, h); ObjectSetInteger(0, name, OBJPROP_BGCOLOR, panelBackgroundColor); ObjectSetInteger(0, name, OBJPROP_COLOR, panelButtonTextColor); ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, borderClr); ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 11); ObjectSetString(0, name, OBJPROP_FONT, "Arial Bold"); ObjectSetString(0, name, OBJPROP_TEXT, text); ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, name, OBJPROP_STATE, false); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); } /** * Remove All Drawn Objects ... */ void RemoveUIObjects() { // long cid = ChartID(); for (int i = ObjectsTotal(cid, 0) - 1; i >= 0; i--) { // string name = ObjectName(cid, i, 0); if (StringFind(name, ShortName + "_") >= 0) { ObjectDelete(cid, name); } } // ChartRedraw(); } /** * Update Information Label ... */ void UpdateInfoLabel() { // int totalBars = Bars(_Symbol, _Period); int shownBars = totalBars - xReplyCurrentBarIndex; // datetime barTime = iTime(_Symbol, _Period, xReplyCurrentBarIndex); string status = xReplyIsPlaying ? "PLAYING" : "PAUSED"; // string info = StringFormat( "Bar: %d / %d | %s | %s", shownBars, totalBars, TimeToString(barTime, TIME_DATE | TIME_MINUTES), status // ); // ObjectSetString(0, xReplyLblInfo, OBJPROP_TEXT, info); // ChartRedraw(); } /** * Handle Show Visible Bars ... */ void DrawVisibleCandles() { // if (ArraySize(pBarColorBuffer) <= 0) { return; } // UpdateVisibleColors(); // // Track drawn count for future optimization ... xReplyLastDrawnBarCount = xReplyCurrentVisibleBars; // ChartRedraw(); } /** * Called after OHLC buffers are filled in OnCalculate */ void UpdateVisibleColors() { // int bufferSize = ArraySize(pBarColorBuffer); if (bufferSize <= 0) { return; } // \ for (int i = 0; i < xReplyCurrentBarIndex && i < bufferSize; i++) { pBarColorBuffer[i] = hideColorIDX; } // for (int i = xReplyCurrentBarIndex; i < bufferSize; i++) { // bool isBullish = pBarCloseBuffer[i] >= pBarOpenBuffer[i]; pBarColorBuffer[i] = isBullish ? bullishColorIDX : bearishColorIDX; } // ChartRedraw(); } // #endregion // #region Control Actions ... /** * Handle Step Forward Action ... */ void StepForward() { // if (xReplyCurrentBarIndex > 0) { // xReplyCurrentBarIndex--; DrawVisibleCandles(); UpdateInfoLabel(); ScrollToBar(xReplyCurrentBarIndex); } } /** * Handle Step Backward Action ... */ void StepBackward() { // int totalBars = Bars(_Symbol, _Period); if (xReplyCurrentBarIndex < totalBars - 1) { // xReplyCurrentBarIndex++; DrawVisibleCandles(); UpdateInfoLabel(); ScrollToBar(xReplyCurrentBarIndex); } } /** * Handle Reset Reply Action ... */ void ResetReplay() { // xReplyCurrentBarIndex = xReplyStartBarIndex; xReplyIsPlaying = false; // ObjectSetInteger(0, xReplyBtnPlay, OBJPROP_STATE, false); ObjectSetInteger(0, xReplyBtnPause, OBJPROP_STATE, true); // DrawVisibleCandles(); UpdateInfoLabel(); // ScrollToBar(xReplyStartBarIndex); } /** * Scroll Chart to Specified Bar Index ... * * @param barIndex: int, Specified Bar Index ... */ void ScrollToBar(int barIndex) { // // Validate Bar Index ... int totalBars = Bars(_Symbol, _Period); if (barIndex < 0 || barIndex >= totalBars) { return; } // // Set First Visible Bar to Specified Index ... ChartSetInteger(ChartID(), CHART_FIRST_VISIBLE_BAR, barIndex); ChartRedraw(); } // #endregion // #region TimeFrame Change State Management ... /** * Save Replay State for TimeFrame Change ... * Called in OnDeinit when REASON_CHARTCHANGE */ void SaveReplayStateForTimeFrameChange() { // // Calculate Last Visible Bar Index ... int lastVisibleBarIndex = xReplyStartBarIndex - xReplyCurrentVisibleBars + 1; if (lastVisibleBarIndex < 0) { lastVisibleBarIndex = 0; } // // Retrieve Last Visible Bar Time ... datetime lastVisibleTime = iTime(_Symbol, _Period, lastVisibleBarIndex); // // Validate Time ... if (!IsXValid(lastVisibleTime)) { return; } // // Generate Global Variable Names ... string gvTimeName = ShortName + _Symbol + GV_TIME_SUFFIX; string gvActiveName = ShortName + _Symbol + GV_ACTIVE_SUFFIX; string gvVisibleBarsName = ShortName + _Symbol + GV_VISIBLE_BARS_SUFFIX; // // Save Last Visible Time ... GlobalVariableSet(gvTimeName, (double)lastVisibleTime); // // Save Active State ... GlobalVariableSet(gvActiveName, 1.0); // // Save Visible Bars Count ... GlobalVariableSet(gvVisibleBarsName, (double)xReplyCurrentVisibleBars); } /** * Try to Resume Replay from TimeFrame Change ... * Called in OnInit to check if we should resume * * @return ( bool ) - true if resumed successfully */ bool TryResumeFromTimeFrameChange() { // bool result = false; // // Generate Global Variable Names ... string gvTimeName = ShortName + _Symbol + GV_TIME_SUFFIX; string gvActiveName = ShortName + _Symbol + GV_ACTIVE_SUFFIX; string gvVisibleBarsName = ShortName + _Symbol + GV_VISIBLE_BARS_SUFFIX; // // Check Global Variables Exists ... bool wasActive = GlobalVariableCheck(gvActiveName); bool hasSavedTime = GlobalVariableCheck(gvTimeName); // result = wasActive && hasSavedTime; if (!result) { // // Clean up if partial state exists ... RemoveReplayGlobalVariables(); return result; } // // Retrieve Saved Time ... double savedTimeDouble = GlobalVariableGet(gvTimeName); datetime savedTime = (datetime)savedTimeDouble; // // Validate Saved Time ... result = IsXValid(savedTime); if (!result) { // // Clean up if partial state exists ... RemoveReplayGlobalVariables(); return result; } // // Find First Bar in New TimeFrame at or after Saved Time ... // iBarShift with exact=false returns the bar containing or before the time int newBarIndex = iBarShift(_Symbol, _Period, savedTime, false); // // Validate New Bar Index ... result = newBarIndex >= 0; if (!result) { // // Clean up if partial state exists ... RemoveReplayGlobalVariables(); return result; } // // Set Replay State for New TimeFrame ... xReplyStartTime = iTime(_Symbol, _Period, newBarIndex); xReplyStartBarIndex = newBarIndex; // // Try to restore visible bars count ... if (GlobalVariableCheck(gvVisibleBarsName)) { // double savedVisibleBars = GlobalVariableGet(gvVisibleBarsName); xReplyCurrentVisibleBars = (int)savedVisibleBars; // // Clamp to valid range ... int maxBars = xReplyStartBarIndex + 1; if (xReplyCurrentVisibleBars > maxBars) { xReplyCurrentVisibleBars = maxBars; } if (xReplyCurrentVisibleBars < 1) { xReplyCurrentVisibleBars = 1; } } else { xReplyCurrentVisibleBars = 1; } // // Clean Global Variables after successful restore ... RemoveReplayGlobalVariables(); // return result; } /** * Remove Replay Global Variables ... */ void RemoveReplayGlobalVariables() { // string gvTimeName = ShortName + _Symbol + GV_TIME_SUFFIX; if (GlobalVariableCheck(gvTimeName)) { GlobalVariableDel(gvTimeName); } // string gvActiveName = ShortName + _Symbol + GV_ACTIVE_SUFFIX; if (GlobalVariableCheck(gvActiveName)) { GlobalVariableDel(gvActiveName); } // string gvVisibleBarsName = ShortName + _Symbol + GV_VISIBLE_BARS_SUFFIX; if (GlobalVariableCheck(gvVisibleBarsName)) { GlobalVariableDel(gvVisibleBarsName); } } // #endregion