Initial commit: MQL5 Indicators Collection (MetaTrader 5) - Part 4
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# 🚀 Unlock the Power of Trading!
|
||||
|
||||
Welcome to this open-source trading project. Here you will find powerful tools to enhance your trading journey. If you find this project useful, please consider starring ⭐, sharing, or donating to support further development!
|
||||
|
||||
---
|
||||
|
||||
**Support the project:**
|
||||
- Star this repository on GitHub
|
||||
- Share it with your trading friends
|
||||
- [Donate here](https://www.paypal.com/donate/?hosted_button_id=YOUR_BUTTON_ID) to help us grow!
|
||||
|
||||
---
|
||||
|
||||
## Files included:
|
||||
### Source Files:
|
||||
- `unity.mq5`
|
||||
|
||||
### Screenshots:
|
||||

|
||||

|
||||
|
||||
> Made with ❤️ for the trading community.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,418 @@
|
||||
//|------------------------------------------------------------------+
|
||||
//| Unity.mq5 |
|
||||
//| Copyright (c) 2018, Marketeer |
|
||||
//| https://www.mql5.com/en/users/marketeer |
|
||||
//| https://www.mql5.com/en/articles/5473/ |
|
||||
//| https://www.mql5.com/en/code/26112 |
|
||||
//|------------------------------------------------------------------+
|
||||
#property copyright "2018 © Marketeer"
|
||||
#property link "https://www.mql5.com/en/users/marketeer"
|
||||
#property version "1.0"
|
||||
#property description "Multi-asset cluster indicator taking all currencies as a sum of squares forming market unity (1.0)"
|
||||
|
||||
|
||||
#define BUF_NUM 15
|
||||
|
||||
|
||||
#property indicator_separate_window
|
||||
#property indicator_buffers BUF_NUM
|
||||
#property indicator_plots BUF_NUM
|
||||
|
||||
#property indicator_color1 Green
|
||||
#property indicator_color2 DarkBlue
|
||||
#property indicator_color3 Red
|
||||
#property indicator_color4 Gray
|
||||
#property indicator_color5 Peru
|
||||
#property indicator_color6 Gold
|
||||
#property indicator_color7 Purple
|
||||
#property indicator_color8 Teal
|
||||
|
||||
#property indicator_color9 LightGreen
|
||||
#property indicator_color10 LightBlue
|
||||
#property indicator_color11 Orange
|
||||
#property indicator_color12 LightGray
|
||||
#property indicator_color13 Brown
|
||||
#property indicator_color14 Yellow
|
||||
#property indicator_color15 Magenta
|
||||
|
||||
|
||||
input string _1 = ""; // Cluster Indicator Settings
|
||||
input string Instruments = "EURUSD,GBPUSD,USDCHF,USDJPY,AUDUSD,USDCAD,NZDUSD"; // · Instruments
|
||||
input int BarLimit = 500; // · BarLimit
|
||||
input ENUM_DRAW_TYPE Draw = DRAW_LINE; // · Draw
|
||||
input ENUM_APPLIED_PRICE PriceType = PRICE_CLOSE; // · PriceType
|
||||
input bool AbsoluteValues = false; // · AbsoluteValues
|
||||
|
||||
input string _2 = ""; // Data Export to CSV
|
||||
input string SaveToFile = ""; // · SaveToFile
|
||||
input bool ShiftLastBuffer = false; // · ShiftLastBuffer
|
||||
input int BarLookback = 1; // · BarLookback
|
||||
|
||||
int ShiftedBuffer = -1;
|
||||
|
||||
bool initDone = false;
|
||||
|
||||
#include <CSOM/IndArray.mqh>
|
||||
#include <CSOM/HashMapTemplate.mqh>
|
||||
|
||||
IndicatorArray buffers(BUF_NUM);
|
||||
IndicatorArrayGetter getter(buffers);
|
||||
|
||||
string Symbols[];
|
||||
int Direction[];
|
||||
double Contracts[];
|
||||
int SymbolCount;
|
||||
|
||||
HashMapTemplate<string,int> workCurrencies;
|
||||
int baseIndex = 0;
|
||||
string BaseCurrency;
|
||||
int LastBarCount;
|
||||
|
||||
bool initSymbols()
|
||||
{
|
||||
SymbolCount = StringSplit(Instruments, ',', Symbols);
|
||||
if(SymbolCount >= BUF_NUM)
|
||||
{
|
||||
SymbolCount = BUF_NUM - 1;
|
||||
ArrayResize(Symbols, SymbolCount);
|
||||
}
|
||||
else if(SymbolCount == 0)
|
||||
{
|
||||
SymbolCount = 1;
|
||||
ArrayResize(Symbols, SymbolCount);
|
||||
Symbols[0] = Symbol();
|
||||
}
|
||||
|
||||
ArrayResize(Direction, SymbolCount);
|
||||
ArrayInitialize(Direction, 0);
|
||||
ArrayResize(Contracts, SymbolCount);
|
||||
ArrayInitialize(Contracts, 1);
|
||||
|
||||
string base = NULL;
|
||||
|
||||
for(int i = 0; i < SymbolCount; i++)
|
||||
{
|
||||
if(!SymbolSelect(Symbols[i], true))
|
||||
{
|
||||
Print("Can't select ", Symbols[i]);
|
||||
return false;
|
||||
}
|
||||
|
||||
string first, second;
|
||||
first = SymbolInfoString(Symbols[i], SYMBOL_CURRENCY_BASE);
|
||||
second = SymbolInfoString(Symbols[i], SYMBOL_CURRENCY_PROFIT);
|
||||
|
||||
if(first != second)
|
||||
{
|
||||
workCurrencies.inc(first, 1);
|
||||
workCurrencies.inc(second, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
workCurrencies.inc(Symbols[i], 1);
|
||||
}
|
||||
}
|
||||
|
||||
if(workCurrencies.getSize() >= BUF_NUM)
|
||||
{
|
||||
Print("Too many symbols, max ", (BUF_NUM - 1));
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ShiftLastBuffer)
|
||||
{
|
||||
ShiftedBuffer = workCurrencies.getSize() - 1;
|
||||
}
|
||||
|
||||
for(int i = 0; i < workCurrencies.getSize(); i++)
|
||||
{
|
||||
if(workCurrencies[i] > 1)
|
||||
{
|
||||
if(base == NULL)
|
||||
{
|
||||
base = workCurrencies.getKey(i);
|
||||
baseIndex = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Collision: multiple base symbols: ", base, "[", workCurrencies[base], "] ", workCurrencies.getKey(i), "[", workCurrencies[i], "]");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BaseCurrency = base;
|
||||
|
||||
for(int i = 0; i < SymbolCount; i++)
|
||||
{
|
||||
if(SymbolInfoString(Symbols[i], SYMBOL_CURRENCY_PROFIT) == base) Direction[i] = +1;
|
||||
else if(SymbolInfoString(Symbols[i], SYMBOL_CURRENCY_BASE) == base) Direction[i] = -1;
|
||||
else
|
||||
{
|
||||
Print("Ambiguous symbol direction ", Symbols[i], ", defaults used");
|
||||
Direction[i] = +1;
|
||||
}
|
||||
|
||||
Contracts[i] = SymbolInfoDouble(Symbols[i], SYMBOL_TRADE_CONTRACT_SIZE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
ShiftedBuffer = -1;
|
||||
initDone = false;
|
||||
|
||||
if(!initSymbols()) return INIT_PARAMETERS_INCORRECT;
|
||||
|
||||
string base = SymbolInfoString(_Symbol, SYMBOL_CURRENCY_BASE);
|
||||
string profit = SymbolInfoString(_Symbol, SYMBOL_CURRENCY_PROFIT);
|
||||
|
||||
int replaceIndex = -1;
|
||||
for(int i = 0; i <= SymbolCount; i++)
|
||||
{
|
||||
string name;
|
||||
if(i == 0)
|
||||
{
|
||||
name = BaseCurrency;
|
||||
if(name != workCurrencies.getKey(i))
|
||||
{
|
||||
replaceIndex = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(BaseCurrency == workCurrencies.getKey(i) && replaceIndex > -1)
|
||||
{
|
||||
name = workCurrencies.getKey(replaceIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = workCurrencies.getKey(i);
|
||||
}
|
||||
}
|
||||
|
||||
PlotIndexSetString(i, PLOT_LABEL, name);
|
||||
int width = (name == base || name == profit) ? 2 : 1;
|
||||
PlotIndexSetInteger(i, PLOT_DRAW_TYPE, Draw);
|
||||
PlotIndexSetInteger(i, PLOT_LINE_WIDTH, width);
|
||||
PlotIndexSetInteger(i, PLOT_SHOW_DATA, true);
|
||||
}
|
||||
|
||||
for(int i = SymbolCount + 1; i < BUF_NUM; i++)
|
||||
{
|
||||
PlotIndexSetInteger(i, PLOT_SHOW_DATA, false);
|
||||
}
|
||||
|
||||
if(!AbsoluteValues)
|
||||
{
|
||||
IndicatorSetInteger(INDICATOR_LEVELS, 1);
|
||||
IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, 0.0);
|
||||
}
|
||||
|
||||
IndicatorSetString(INDICATOR_SHORTNAME, "Unity [" + (string)workCurrencies.getSize() + "] " + EnumToString(PriceType));
|
||||
IndicatorSetInteger(INDICATOR_DIGITS, 5);
|
||||
|
||||
LastBarCount = 0;
|
||||
initDone = true;
|
||||
|
||||
EventSetTimer(1);
|
||||
|
||||
Print("Unity ", __FUNCTION__, " ", Bars(_Symbol, PERIOD_CURRENT));
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
#define CopyXYZ(N, S, B, A) if(Copy##N(S, PERIOD_CURRENT, B, 1, A) != 1) {Print("Failed Copy", #N, " ", S, " ", B, " ", GetLastError()); return false;}
|
||||
#define CopyXYZS(N, B, A) if(Copy##N(_Symbol, PERIOD_CURRENT, B, 1, A) != 1) {Print("Failed Copy", #N, " ", _Symbol, " ", B, " ", GetLastError()); return false;}
|
||||
#define CopyXYZN(N, B, C, A) if(Copy##N(_Symbol, PERIOD_CURRENT, B, C, A) != C) {Print("Failed Copy", #N, " ", _Symbol, " ", B, " ", GetLastError()); return false;}
|
||||
#define CopyXYZSN(N, S, B, C, A) if(Copy##N(S, PERIOD_CURRENT, B, C, A) != C) {Print("Failed Copy", #N, " ", S, " ", B, " ", GetLastError()); return false;}
|
||||
|
||||
#define TOSTRING(A) #A + " = " + (string)(A) + " "
|
||||
|
||||
bool calculate(const int bar)
|
||||
{
|
||||
datetime time[1];
|
||||
CopyXYZN(Time, bar, 1, time);
|
||||
|
||||
double w[2] = {0};
|
||||
double v[][2];
|
||||
ArrayResize(v, SymbolCount);
|
||||
|
||||
for(int j = 0; j < SymbolCount; j++)
|
||||
{
|
||||
if(PriceType == PRICE_OPEN)
|
||||
{
|
||||
CopyXYZSN(Open, Symbols[j], time[0], 2, w);
|
||||
}
|
||||
else
|
||||
if(PriceType == PRICE_CLOSE)
|
||||
{
|
||||
CopyXYZSN(Close, Symbols[j], time[0], 2, w);
|
||||
}
|
||||
|
||||
if(Direction[j] == -1)
|
||||
{
|
||||
w[0] = 1.0 / w[0];
|
||||
w[1] = 1.0 / w[1];
|
||||
}
|
||||
|
||||
w[1] *= Contracts[j];
|
||||
w[0] *= Contracts[j];
|
||||
|
||||
v[j][0] = w[1];
|
||||
v[j][1] = w[0];
|
||||
}
|
||||
|
||||
double sum[2] = {1.0, 1.0};
|
||||
|
||||
for(int j = 0; j < SymbolCount; j++)
|
||||
{
|
||||
sum[0] += v[j][0] * v[j][0];
|
||||
sum[1] += v[j][1] * v[j][1];
|
||||
}
|
||||
|
||||
double base2_0 = 1.0 / sum[0];
|
||||
double base2_1 = 1.0 / sum[1];
|
||||
|
||||
double base_0 = MathSqrt(base2_0);
|
||||
double base_1 = MathSqrt(base2_1);
|
||||
|
||||
if(AbsoluteValues)
|
||||
{
|
||||
buffers[0][bar] = base_0 * Contracts[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
buffers[0][bar] = base_0 / base_1 - 1.0;
|
||||
}
|
||||
|
||||
for(int j = 1; j <= SymbolCount; j++)
|
||||
{
|
||||
if(AbsoluteValues)
|
||||
{
|
||||
buffers[j][bar] = base_0 * v[j - 1][0];
|
||||
}
|
||||
else
|
||||
{
|
||||
buffers[j][bar] = base_0 * v[j - 1][0] / (base_1 * v[j - 1][1]) - 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnTimer()
|
||||
{
|
||||
const double price[] = {};
|
||||
|
||||
if(LastBarCount != Bars(_Symbol, PERIOD_CURRENT))
|
||||
{
|
||||
int n = OnCalculate(Bars(_Symbol, PERIOD_CURRENT), 0, 0, price);
|
||||
if(n > 0)
|
||||
{
|
||||
EventKillTimer();
|
||||
ChartSetSymbolPeriod(0, _Symbol, PERIOD_CURRENT);
|
||||
ChartRedraw();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EventKillTimer();
|
||||
}
|
||||
}
|
||||
|
||||
int OnCalculate(const int rates_total,
|
||||
const int prev_calculated,
|
||||
const int begin,
|
||||
const double& price[])
|
||||
{
|
||||
if(!initDone) return 0;
|
||||
|
||||
if(LastBarCount == rates_total && prev_calculated == rates_total && PriceType != PRICE_CLOSE)
|
||||
{
|
||||
return prev_calculated;
|
||||
}
|
||||
|
||||
if(prev_calculated == 0)
|
||||
{
|
||||
for(int i = 0; i < rates_total; i++)
|
||||
{
|
||||
for(int j = 0; j < BUF_NUM; j++)
|
||||
{
|
||||
buffers[j][i] = EMPTY_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int limit = MathMin(rates_total - prev_calculated + 1, BarLimit);
|
||||
|
||||
for(int i = 0; i < limit; i++)
|
||||
{
|
||||
if(!calculate(i))
|
||||
{
|
||||
return 0; // will retry on next tick
|
||||
}
|
||||
}
|
||||
|
||||
static bool fileSaved = SaveToFile == "";
|
||||
if(!fileSaved && !AbsoluteValues)
|
||||
{
|
||||
SaveBuffersToFile(SaveToFile);
|
||||
fileSaved = true;
|
||||
}
|
||||
|
||||
LastBarCount = rates_total;
|
||||
|
||||
return LastBarCount;
|
||||
}
|
||||
|
||||
double GetBuffer(int index, int bar)
|
||||
{
|
||||
return getter[index][bar];
|
||||
}
|
||||
|
||||
bool SaveBuffersToFile(const string filename)
|
||||
{
|
||||
int h = FileOpen(filename, FILE_WRITE | FILE_CSV | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_ANSI, ';');
|
||||
if(h == INVALID_HANDLE) return false;
|
||||
|
||||
string line = "datetime";
|
||||
for(int k = BarLookback - 1; k >= 0; k--)
|
||||
{
|
||||
for(int i = 0; i < workCurrencies.getSize(); i++)
|
||||
{
|
||||
line += ";" + workCurrencies.getKey(i) + (string)(k + 1);
|
||||
}
|
||||
}
|
||||
if(ShiftLastBuffer)
|
||||
{
|
||||
line += ";FORECAST";
|
||||
}
|
||||
FileWriteString(h, line + "\n");
|
||||
|
||||
for(int i = BarLimit - BarLookback; i >= (ShiftedBuffer > -1 ? 1 : 0); i--)
|
||||
{
|
||||
datetime time[1];
|
||||
CopyXYZN(Time, i, 1, time);
|
||||
line = (string)time[0];
|
||||
for(int k = BarLookback - 1; k >= 0; k--)
|
||||
{
|
||||
for(int j = 0; j < workCurrencies.getSize(); j++)
|
||||
{
|
||||
line += ";" + (string)GetBuffer(j, i + k);
|
||||
}
|
||||
}
|
||||
if(ShiftLastBuffer)
|
||||
{
|
||||
line += ";" + (string)GetBuffer(ShiftedBuffer, i - 1); // look into the future for 1 bar
|
||||
}
|
||||
FileWriteString(h, line + "\n");
|
||||
}
|
||||
|
||||
FileClose(h);
|
||||
|
||||
Print("File saved ", filename);
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user