Parabolic SAR Combo Crossover by ahmedalhoseny
49567 downloads / 5407 views / Created: 15.12.2013 Average Rating: 5
Indicator Description
Parabolic SAR Combo Crossover
Comments
//==============================================================
// Forex Strategy Builder
// Copyright © Miroslav Popov. All rights reserved.
//==============================================================
// THIS CODE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE.
//==============================================================
using System;
using System.Drawing;
using ForexStrategyBuilder.Infrastructure.Entities;
using ForexStrategyBuilder.Infrastructure.Enums;
using ForexStrategyBuilder.Infrastructure.Interfaces;
namespace ForexStrategyBuilder.Indicators.Store
{
public class PsrCrossover : Indicator
{
public PsrCrossover()
{
IndicatorName = "Parabolic SAR Combo Crossover";
PossibleSlots = SlotTypes.OpenFilter | SlotTypes.CloseFilter;
IndicatorAuthor = "Ahmed Alhoseny";
IndicatorVersion = "2.0";
IndicatorDescription = "Custom Indicator.";
}
public override void Initialize(SlotTypes slotType)
{
SlotType = slotType;
// The ComboBox parameters
IndParam.ListParam[0].Caption = "Logic";
IndParam.ListParam[0].ItemList = new[]
{
"Psar1 crosses Psar2 upward",
"Psar1 crosses Psar2 downward",
"Psar1 is higher than Psar2",
"Psar1 is lower than Psar2"
};
IndParam.ListParam[0].Index = 0;
IndParam.ListParam[0].Text = IndParam.ListParam[0].ItemList[IndParam.ListParam[0].Index];
IndParam.ListParam[0].Enabled = true;
IndParam.ListParam[0].ToolTip = "Logic of application of the indicator.";
// The NumericUpDown parameters
IndParam.NumParam[0].Caption = "Starting AF1";
IndParam.NumParam[0].Value = 0.02;
IndParam.NumParam[0].Min = 0.00;
IndParam.NumParam[0].Max = 1.00;
IndParam.NumParam[0].Point = 2;
IndParam.NumParam[0].Enabled = true;
IndParam.NumParam[0].ToolTip = "The starting value of Acceleration Factor.";
IndParam.NumParam[1].Caption = "Starting AF2";
IndParam.NumParam[1].Value = 0.02;
IndParam.NumParam[1].Min = 0.00;
IndParam.NumParam[1].Max = 1.00;
IndParam.NumParam[1].Point = 2;
IndParam.NumParam[1].Enabled = true;
IndParam.NumParam[1].ToolTip = "The starting value of Acceleration Factor.";
IndParam.NumParam[2].Caption = "Increment1";
IndParam.NumParam[2].Value = 0.02;
IndParam.NumParam[2].Min = 0.01;
IndParam.NumParam[2].Max = 1.00;
IndParam.NumParam[2].Point = 2;
IndParam.NumParam[2].Enabled = true;
IndParam.NumParam[2].ToolTip = "Increment value.";
IndParam.NumParam[3].Caption = "Increment2";
IndParam.NumParam[3].Value = 0.02;
IndParam.NumParam[3].Min = 0.01;
IndParam.NumParam[3].Max = 1.00;
IndParam.NumParam[3].Point = 2;
IndParam.NumParam[3].Enabled = true;
IndParam.NumParam[3].ToolTip = "Increment value.";
IndParam.NumParam[4].Caption = "Maximum AF1";
IndParam.NumParam[4].Value = 0.01;
IndParam.NumParam[4].Min = 0.01;
IndParam.NumParam[4].Max = 2.00;
IndParam.NumParam[4].Point = 2;
IndParam.NumParam[4].Enabled = true;
IndParam.NumParam[4].ToolTip = "The maximum value of the Acceleration Factor.";
IndParam.NumParam[5].Caption = "Maximum AF2";
IndParam.NumParam[5].Value = 0.01;
IndParam.NumParam[5].Min = 0.01;
IndParam.NumParam[5].Max = 2.00;
IndParam.NumParam[5].Point = 2;
IndParam.NumParam[5].Enabled = true;
IndParam.NumParam[5].ToolTip = "The maximum value of the Acceleration Factor.";
// The CheckBox parameters
IndParam.CheckParam[0].Caption = "Use previous bar value";
IndParam.CheckParam[0].Enabled = true;
IndParam.CheckParam[0].ToolTip = "Use the indicator value from the previous bar.";
}
public override void Calculate(IDataSet dataSet)
{
DataSet = dataSet;
// Reading the parameters
double dAfMin1 = IndParam.NumParam[0].Value;
double dAfMin2 = IndParam.NumParam[1].Value;
double dAfInc1 = IndParam.NumParam[2].Value;
double dAfInc2 = IndParam.NumParam[3].Value;
double dAfMax1 = IndParam.NumParam[4].Value;
double dAfMax2 = IndParam.NumParam[5].Value;
int previous = IndParam.CheckParam[0].Checked ? 1 : 0;
// Reading the parameters
double dPExtr1;
double dPExtr2;
double dadPsarNew1 = 0;
double dadPsarNew2 = 0;
var aiDir1 = new int[Bars];
var aiDir2 = new int[Bars];
var adPsar1 = new double[Bars];
var adPsar2 = new double[Bars];
//---- Calculating the initial values
adPsar1[0] = 0;
adPsar2[0] = 0;
double dAf1 = dAfMin1;
double dAf2 = dAfMin2;
int intDirNew1 = 0;
int intDirNew2 = 0;
if (Close[1] > Open[0])
{
aiDir1[0] = 1;
aiDir2[0] = 1;
aiDir1[1] = 1;
aiDir2[1] = 1;
dPExtr1 = Math.Max(High[0], High[1]);
dPExtr2 = Math.Max(High[0], High[1]);
adPsar1[1] = Math.Min(Low[0], Low[1]);
adPsar2[1] = Math.Min(Low[0], Low[1]);
}
else
{
aiDir1[0] = -1;
aiDir2[0] = -1;
aiDir1[1] = -1;
aiDir2[1] = -1;
dPExtr1 = Math.Min(Low[0], Low[1]);
dPExtr2 = Math.Min(Low[0], Low[1]);
adPsar1[1] = Math.Max(High[0], High[1]);
adPsar2[1] = Math.Max(High[0], High[1]);
}
for (int iBar = 2; iBar < Bars; iBar++)
{
//---- adPsar for the current period
if (intDirNew1 != 0)
{
// The direction was changed during the last period
aiDir1[iBar] = intDirNew1;
intDirNew1 = 0;
adPsar1[iBar] = dadPsarNew1 + dAf1 * (dPExtr1 - dadPsarNew1);
}
else
{
aiDir1[iBar] = aiDir1[iBar - 1];
adPsar1[iBar] = adPsar1[iBar - 1] + dAf1 * (dPExtr1 - adPsar1[iBar - 1]);
}
//----------------------------------------------------------------------------
if (intDirNew2 != 0)
{
// The direction was changed during the last period
aiDir2[iBar] = intDirNew2;
intDirNew2 = 0;
adPsar2[iBar] = dadPsarNew2 + dAf2 * (dPExtr2 - dadPsarNew2);
}
else
{
aiDir2[iBar] = aiDir2[iBar - 1];
adPsar2[iBar] = adPsar2[iBar - 1] + dAf2 * (dPExtr2 - adPsar2[iBar - 1]);
}
// adPsar has to be out of the previous two bars limits
if (aiDir1[iBar] > 0 && adPsar1[iBar] > Math.Min(Low[iBar - 1], Low[iBar - 2]))
adPsar1[iBar] = Math.Min(Low[iBar - 1], Low[iBar - 2]);
else if (aiDir1[iBar] < 0 && adPsar1[iBar] < Math.Max(High[iBar - 1], High[iBar - 2]))
adPsar1[iBar] = Math.Max(High[iBar - 1], High[iBar - 2]);
//-------------------------------------------------------------------------------------------
if (aiDir2[iBar] > 0 && adPsar2[iBar] > Math.Min(Low[iBar - 1], Low[iBar - 2]))
adPsar2[iBar] = Math.Min(Low[iBar - 1], Low[iBar - 2]);
else if (aiDir2[iBar] < 0 && adPsar2[iBar] < Math.Max(High[iBar - 1], High[iBar - 2]))
adPsar2[iBar] = Math.Max(High[iBar - 1], High[iBar - 2]);
//---- adPsar for the next period
// Calculation of the new values of flPExtr and flAF
// if there is a new extreme price in the adPsar direction
if (aiDir1[iBar] > 0 && High[iBar] > dPExtr1)
{
dPExtr1 = High[iBar];
dAf1 = Math.Min(dAf1 + dAfInc1, dAfMax1);
}
if (aiDir1[iBar] < 0 && Low[iBar] < dPExtr1)
{
dPExtr1 = Low[iBar];
dAf1 = Math.Min(dAf1 + dAfInc1, dAfMax1);
}
//-------------------------------------------
if (aiDir2[iBar] > 0 && High[iBar] > dPExtr2)
{
dPExtr2 = High[iBar];
dAf2 = Math.Min(dAf2 + dAfInc2, dAfMax2);
}
if (aiDir2[iBar] < 0 && Low[iBar] < dPExtr2)
{
dPExtr2 = Low[iBar];
dAf2 = Math.Min(dAf2 + dAfInc2, dAfMax2);
}
// Whether the price reaches adPsar
if (Low[iBar] <= adPsar1[iBar] && adPsar1[iBar] <= High[iBar])
{
intDirNew1 = -aiDir1[iBar];
dadPsarNew1 = dPExtr1;
dAf1 = dAfMin1;
dPExtr1 = intDirNew1 > 0 ? High[iBar] : Low[iBar];
}
//---------------------------------------------------------------
if (Low[iBar] <= adPsar2[iBar] && adPsar2[iBar] <= High[iBar])
{
intDirNew2 = -aiDir2[iBar];
dadPsarNew2 = dPExtr2;
dAf2 = dAfMin2;
dPExtr2 = intDirNew2 > 0 ? High[iBar] : Low[iBar];
}
}
const int firstBar = 8 ;
var adMAOscillator = new double[Bars];
for (int iBar = 8; iBar < Bars; iBar++)
adMAOscillator[iBar] = adPsar1[iBar] - adPsar2[iBar];
// Saving the components
Component = new IndicatorComp[4];
Component[0] = new IndicatorComp
{
CompName = "Fast Moving Average",
ChartColor = Color.Goldenrod,
DataType = IndComponentType.IndicatorValue,
ChartType = IndChartType.Line,
FirstBar = 8,
Value = adPsar1
};
Component[1] = new IndicatorComp
{
CompName = "Slow Moving Average",
ChartColor = Color.IndianRed,
DataType = IndComponentType.IndicatorValue,
ChartType = IndChartType.Line,
FirstBar = 8,
Value = adPsar2
};
Component[2] = new IndicatorComp
{
ChartType = IndChartType.NoChart,
FirstBar = 8,
Value = new double[Bars]
};
Component[3] = new IndicatorComp
{
ChartType = IndChartType.NoChart,
FirstBar = 8,
Value = new double[Bars]
};
// Sets the Component's type
if (SlotType == SlotTypes.OpenFilter)
{
Component[2].DataType = IndComponentType.AllowOpenLong;
Component[2].CompName = "Is long entry allowed";
Component[3].DataType = IndComponentType.AllowOpenShort;
Component[3].CompName = "Is short entry allowed";
}
else if (SlotType == SlotTypes.CloseFilter)
{
Component[2].DataType = IndComponentType.ForceCloseLong;
Component[2].CompName = "Close out long position";
Component[3].DataType = IndComponentType.ForceCloseShort;
Component[3].CompName = "Close out short position";
}
// Calculation of the logic
var indLogic = IndicatorLogic.It_does_not_act_as_a_filter;
switch (IndParam.ListParam[0].Text)
{
case "Psar1 crosses Psar2 upward":
indLogic = IndicatorLogic.The_indicator_crosses_the_level_line_upward;
break;
case "Psar1 crosses Psar2 downward":
indLogic = IndicatorLogic.The_indicator_crosses_the_level_line_downward;
break;
case "Psar1 is higher than Psar2":
indLogic = IndicatorLogic.The_indicator_is_higher_than_the_level_line;
break;
case "Psar1 is lower than Psar2":
indLogic = IndicatorLogic.The_indicator_is_lower_than_the_level_line;
break;
}
OscillatorLogic(8, previous, adMAOscillator, 0, 0, ref Component[2], ref Component[3], indLogic);
}
public override void SetDescription()
{
EntryFilterLongDescription = ToString() + "; Psar1 ";
EntryFilterShortDescription = ToString() + "; Psar1 ";
ExitFilterLongDescription = ToString() + "; Psar1 ";
ExitFilterShortDescription = ToString() + "; Psar1 ";
switch (IndParam.ListParam[0].Text)
{
case "Psar1 crosses Psar2 upward":
EntryFilterLongDescription += "crosses Psar2 upward";
EntryFilterShortDescription += "crosses Psar2 downward";
ExitFilterLongDescription += "crosses Psar2 upward";
ExitFilterShortDescription += "crosses Psar2 downward";
break;
case "Psar1 crosses Psar2 downward":
EntryFilterLongDescription += "crosses Psar2 downward";
EntryFilterShortDescription += "crosses Psar2 upward";
ExitFilterLongDescription += "crosses Psar2 downward";
ExitFilterShortDescription += "crosses Psar2 upward";
break;
case "Psar1 is higher than Psar2":
EntryFilterLongDescription += "is higher than Psar2";
EntryFilterShortDescription += "is lower than Psar2";
ExitFilterLongDescription += "is higher than Psar2";
ExitFilterShortDescription += "is lower than Psar2";
break;
case "Psar1 is lower than Psar2":
EntryFilterLongDescription += "is lower than Psar2";
EntryFilterShortDescription += "is higher than Psar2";
ExitFilterLongDescription += "is lower than Psar2";
ExitFilterShortDescription += "is higher than Psar2";
break;
}
}
public override string ToString()
{
return IndicatorName +
(IndParam.CheckParam[0].Checked ? "* (" : " (") +
IndParam.ListParam[1].Text + ", " + // Price
IndParam.ListParam[3].Text + ", " + // Psar1 Method
IndParam.ListParam[4].Text + ", " + // Psar2 Method
IndParam.NumParam[0].ValueToString + ", " + // Psar1 period
IndParam.NumParam[1].ValueToString + ", " + // Psar2 period
IndParam.NumParam[2].ValueToString + ", " + // Psar1 shift
IndParam.NumParam[3].ValueToString + ")"; // Psar2 shift
}
}
}
//+--------------------------------------------------------------------+ //| Copyright: (C) 2014, Miroslav Popov - All rights reserved! | //| Website: http://forexsb.com/ | //| Support: http://forexsb.com/forum/ | //| License: Proprietary under the following circumstances: | //| | //| This code is a part of Forex Strategy Builder. It is free for | //| use as an integral part of Forex Strategy Builder. | //| One can modify it in order to improve the code or to fit it for | //| personal use. This code or any part of it cannot be used in | //| another applications without a permission. Contact information | //| cannot be changed. | //| | //| NO LIABILITY FOR CONSEQUENTIAL DAMAGES | //| | //| In no event shall the author be liable for any damages whatsoever | //| (including, without limitation, incidental, direct, indirect and | //| consequential damages, damages for loss of business profits, | //| business interruption, loss of business information, or other | //| pecuniary loss) arising out of the use or inability to use this | //| product, even if advised of the possibility of such damages. | //+--------------------------------------------------------------------+ #property copyright "Copyright 2014, Miroslav Popov" #property link "http://forexsb.com" #property version "1.00" #property strict #include <Forexsb.com/Indicator.mqh> #include <Forexsb.com/Enumerations.mqh> //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class ParabolicSARComboCrossover : public Indicator { public: ParabolicSARComboCrossover(SlotTypes slotType) { SlotType=slotType; IndicatorName="Parabolic SAR Combo Crossover"; WarningMessage = ""; IsAllowLTF = true; ExecTime = ExecutionTime_DuringTheBar; IsSeparateChart = false; IsDiscreteValues = false; IsDeafultGroupAll = false; } virtual void Calculate(DataSet &dataSet); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void ParabolicSARComboCrossover::Calculate(DataSet &dataSet) { Data=GetPointer(dataSet); // Reading the parameters double dAfMin1 = NumParam[0].Value; double dAfMin2 = NumParam[1].Value; double dAfInc1 = NumParam[2].Value; double dAfInc2 = NumParam[3].Value; double dAfMax1 = NumParam[4].Value; double dAfMax2 = NumParam[5].Value; int previous=CheckParam[0].Checked ? 1 : 0; // Reading the parameters double dPExtr1; double dPExtr2; double dadPsarNew1 = 0; double dadPsarNew2 = 0; int aiDir1[]; ArrayResize(aiDir1,Data.Bars); ArrayInitialize(aiDir1, 0); int aiDir2[]; ArrayResize(aiDir2,Data.Bars); ArrayInitialize(aiDir2, 0); double adPsar1[]; ArrayResize(adPsar1,Data.Bars);ArrayInitialize(adPsar1, 0); double adPsar2[]; ArrayResize(adPsar2,Data.Bars);ArrayInitialize(adPsar2, 0); //---- Calculating the initial values adPsar1[0] = 0; adPsar2[0] = 0; double dAf1 = dAfMin1; double dAf2 = dAfMin2; int intDirNew1 = 0; int intDirNew2 = 0; if(Data.Close[1]>Data.Open[0]) { aiDir1[0] = 1; aiDir2[0] = 1; aiDir1[1] = 1; aiDir2[1] = 1; dPExtr1 = MathMax(Data.High[0], Data.High[1]); dPExtr2 = MathMax(Data.High[0], Data.High[1]); adPsar1[1] = MathMin(Data.Low[0], Data.Low[1]); adPsar2[1] = MathMin(Data.Low[0], Data.Low[1]); } else { aiDir1[0] = -1; aiDir2[0] = -1; aiDir1[1] = -1; aiDir2[1] = -1; dPExtr1 = MathMin(Data.Low[0], Data.Low[1]); dPExtr2 = MathMin(Data.Low[0], Data.Low[1]); adPsar1[1] = MathMax(Data.High[0], Data.High[1]); adPsar2[1] = MathMax(Data.High[0], Data.High[1]); } for(int iBar=2; iBar<Data.Bars; iBar++) { //---- adPsar for the current period if(intDirNew1!=0) { // The direction was changed during the last period aiDir1[iBar]=intDirNew1; intDirNew1=0; adPsar1[iBar]=dadPsarNew1+dAf1 *(dPExtr1-dadPsarNew1); } else { aiDir1[iBar]=aiDir1[iBar-1]; adPsar1[iBar]=adPsar1[iBar-1]+dAf1 *(dPExtr1-adPsar1[iBar-1]); } //---------------------------------------------------------------------------- if(intDirNew2!=0) { // The direction was changed during the last period aiDir2[iBar]=intDirNew2; intDirNew2=0; adPsar2[iBar]=dadPsarNew2+dAf2 *(dPExtr2-dadPsarNew2); } else { aiDir2[iBar]=aiDir2[iBar-1]; adPsar2[iBar]=adPsar2[iBar-1]+dAf2 *(dPExtr2-adPsar2[iBar-1]); } // adPsar has to be out of the previous two bars limits if(aiDir1[iBar]>0 && adPsar1[iBar]>MathMin(Data.Low[iBar-1],Data.Low[iBar-2])) adPsar1[iBar]=MathMin(Data.Low[iBar-1],Data.Low[iBar-2]); else if(aiDir1[iBar]<0 && adPsar1[iBar]<MathMax(Data.High[iBar-1],Data.High[iBar-2])) adPsar1[iBar]=MathMax(Data.High[iBar-1],Data.High[iBar-2]); //------------------------------------------------------------------------------------------- if(aiDir2[iBar]>0 && adPsar2[iBar]>MathMin(Data.Low[iBar-1],Data.Low[iBar-2])) adPsar2[iBar]=MathMin(Data.Low[iBar-1],Data.Low[iBar-2]); else if(aiDir2[iBar]<0 && adPsar2[iBar]<MathMax(Data.High[iBar-1],Data.High[iBar-2])) adPsar2[iBar]=MathMax(Data.High[iBar-1],Data.High[iBar-2]); //---- adPsar for the next period // Calculation of the new values of flPExtr and flAF if(aiDir1[iBar]>0 && Data.High[iBar]>dPExtr1) { dPExtr1=Data.High[iBar]; dAf1=MathMin(dAf1+dAfInc1,dAfMax1); } if(aiDir1[iBar]<0 && Data.Low[iBar]<dPExtr1) { dPExtr1=Data.Low[iBar]; dAf1=MathMin(dAf1+dAfInc1,dAfMax1); } //------------------------------------------- if(aiDir2[iBar]>0 && Data.High[iBar]>dPExtr2) { dPExtr2=Data.High[iBar]; dAf2=MathMin(dAf2+dAfInc2,dAfMax2); } if(aiDir2[iBar]<0 && Data.Low[iBar]<dPExtr2) { dPExtr2=Data.Low[iBar]; dAf2=MathMin(dAf2+dAfInc2,dAfMax2); } // Whether the price reaches adPsar if(Data.Low[iBar]<=adPsar1[iBar] && adPsar1[iBar]<=Data.High[iBar]) { intDirNew1=-aiDir1[iBar]; dadPsarNew1=dPExtr1; dAf1=dAfMin1; dPExtr1=intDirNew1>0 ? Data.High[iBar]: Data.Low[iBar]; } //--------------------------------------------------------------- if(Data.Low[iBar]<=adPsar2[iBar] && adPsar2[iBar]<=Data.High[iBar]) { intDirNew2=-aiDir2[iBar]; dadPsarNew2=dPExtr2; dAf2=dAfMin2; dPExtr2=intDirNew2>0 ? Data.High[iBar]: Data.Low[iBar]; } } double adMAOscillator[]; ArrayResize(adMAOscillator,Data.Bars);ArrayInitialize(adMAOscillator, 0); for(int iBar=8; iBar<Data.Bars; iBar++) adMAOscillator[iBar]=adPsar1[iBar]-adPsar2[iBar]; // Saving the components ArrayResize(Component[0].Value,Data.Bars); Component[0].CompName = "Fast Moving Average"; Component[0].DataType = IndComponentType_IndicatorValue; Component[0].FirstBar = 8; ArrayCopy(Component[0].Value,adPsar1); ArrayResize(Component[1].Value,Data.Bars); Component[1].CompName = "Slow Moving Average"; Component[1].DataType = IndComponentType_IndicatorValue; Component[1].FirstBar = 8; ArrayCopy(Component[1].Value,adPsar2); ArrayResize(Component[2].Value,Data.Bars); Component[2].FirstBar=8; ArrayResize(Component[3].Value,Data.Bars); Component[3].FirstBar=8; // Sets the Component's type if(SlotType==SlotTypes_OpenFilter) { Component[2].DataType = IndComponentType_AllowOpenLong; Component[2].CompName = "Is long entry allowed"; Component[3].DataType = IndComponentType_AllowOpenShort; Component[3].CompName = "Is short entry allowed"; } else if(SlotType==SlotTypes_CloseFilter) { Component[2].DataType = IndComponentType_ForceCloseLong; Component[2].CompName = "Close out long position"; Component[3].DataType = IndComponentType_ForceCloseShort; Component[3].CompName = "Close out short position"; } // Calculation of the logic IndicatorLogic indLogic=IndicatorLogic_It_does_not_act_as_a_filter; if(ListParam[0].Text=="Psar1 crosses Psar2 upward") { indLogic=IndicatorLogic_The_indicator_crosses_the_level_line_upward; } else if(ListParam[0].Text=="Psar1 crosses Psar2 downward") { indLogic=IndicatorLogic_The_indicator_crosses_the_level_line_downward; } else if(ListParam[0].Text=="Psar1 is higher than Psar2") { indLogic=IndicatorLogic_The_indicator_is_higher_than_the_level_line; } else if(ListParam[0].Text=="Psar1 is lower than Psar2") { indLogic=IndicatorLogic_The_indicator_is_lower_than_the_level_line; } OscillatorLogic(8,previous,adMAOscillator,0,0,Component[2],Component[3],indLogic); } //+------------------------------------------------------------------+
Risk warning: Forex, spread bets and CFD are leveraged products. They may not be suitable for you as they carry a high degree of risk to your capital and you can lose more than your initial investment. You should ensure you understand all of the risks.
Copyright © 2006 - 2024, Forex Software Ltd.;
Copyright © 2006 - 2024, Forex Software Ltd.;