Files
MQL5Data/Indicators/x-saherelm.x121.x-reply.mq5

1550 lines
39 KiB
Plaintext

///////////////////////////////////////////////////////
//
// 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