1550 lines
39 KiB
Plaintext
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 |