Gamestudio Links
Zorro Links
Newest Posts
M1 Oversampling
by Petra. 04/24/24 10:34
Zorro FIX plugin - Experimental
by flink. 04/21/24 07:12
Data from CSV not parsed correctly
by EternallyCurious. 04/20/24 21:39
Scripts not found
by juergen_wue. 04/20/24 18:51
zorro 64bit command line support
by 7th_zorro. 04/20/24 10:06
StartWeek not working as it should
by jcl. 04/20/24 08:38
folder management functions
by VoroneTZ. 04/17/24 06:52
AUM Magazine
Latest Screens
The Bible Game
A psychological thriller game
SHADOW (2014)
DEAD TASTE
Who's Online Now
5 registered members (Petra, AndrewAMD, Quad, VoroneTZ, 1 invisible), 488 guests, and 3 spiders.
Key: Admin, Global Mod, Mod
Newest Members
Mega_Rod, EternallyCurious, howardR, 11honza11, ccorrea
19048 Registered Users
Previous Thread
Next Thread
Print Thread
Rate Thread
Page 1 of 2 1 2
SSA implementation #416992
02/06/13 23:16
02/06/13 23:16
Joined: Nov 2012
Posts: 209
S
SFF Offline OP
Member
SFF  Offline OP
Member
S

Joined: Nov 2012
Posts: 209
Hi,

I want to use SSA in Zorro so please teach me how to implement it.
My base code is a MQL file which is libSSA.zip
SSA normalized end-pointed & alert advanced.mq4

Here are files to download.
http://worldwide-invest.org/threads/2376-REQ-CCI-alert?p=15394&viewfull=1#post15394

Thanks in advance.

Last edited by SFF; 02/06/13 23:20.
Re: SSA implementation [Re: SFF] #417023
02/07/13 12:42
02/07/13 12:42
Joined: Jul 2000
Posts: 27,982
Frankfurt
jcl Offline

Chief Engineer
jcl  Offline

Chief Engineer

Joined: Jul 2000
Posts: 27,982
Frankfurt
You need to open the SSA function from the DLL, and feed it with the price data that it needs. I've uploaded the DLL file here separately so that other users can reproduce the script whithout the need to register on other forums.

This is the mq4 file where we can see how the DLL function is called:

Code:
//+--------------------------------------------------------------------------------------+
//|                                                                                      |
//+--------------------------------------------------------------------------------------+
#property copyright "www.forex-tsd.com"
#property link      "www.forex-tsd.com"

#property indicator_separate_window
#property indicator_buffers 6
#property indicator_color1  DimGray
#property indicator_color2  DeepSkyBlue
#property indicator_color3  PaleVioletRed
#property indicator_color4  DeepSkyBlue
#property indicator_color5  PaleVioletRed
#property indicator_color6  PaleVioletRed
#property indicator_width2  2
#property indicator_width3  2
#property indicator_width4  2
#property indicator_width5  2
#property indicator_width6  2
#property indicator_style1 STYLE_DOT

#import "libSSA.dll"
   void fastSingular(double& sourceArray[],int arraySize, int lag, int numberOfComputationLoops, double& destinationArray[]);
#import

//
//
//
//
//

extern int    SSAPrice                =  PRICE_CLOSE;
extern int    SSALag                  = 25;
extern int    SSANumberOfComputations =  2;
extern int    SSAPeriodNormalization  = 10;
extern int    SSANumberOfBars         = 300;
extern int    FirstBar                = 400; 
extern bool   MultiColor              = true;
extern double alertsLevel             = 0.25;
extern bool   alertsOn                = false;
extern bool   alertsOnCurrent         = true;
extern bool   alertsMessage           = true;
extern bool   alertsSound             = false;
extern bool   alertsEmail             = false;

//
//
//
//
//

double in[];
double inDa[];
double inDb[];
double inDotu[];
double inDotd[];
double ssaCurrent[];
double no[];
double ssaIn[];
double ssaOut[];

//+--------------------------------------------------------------------------------------+
//|                                                                                      |
//+--------------------------------------------------------------------------------------+
//
//
//
//
//

int init()
{
   IndicatorBuffers(7);
      SetIndexBuffer(0,ssaCurrent);
      SetIndexBuffer(1,inDotu); SetIndexStyle(1,DRAW_ARROW); SetIndexArrow(1,159);
      SetIndexBuffer(2,inDotd); SetIndexStyle(2,DRAW_ARROW); SetIndexArrow(2,159);
      SetIndexBuffer(3,in);
      SetIndexBuffer(4,inDa);
      SetIndexBuffer(5,inDb);
      SetIndexBuffer(6,no);
         SetLevelValue(0, alertsLevel);
         SetLevelValue(1,-alertsLevel);
         SetLevelValue(2,           0);
   IndicatorShortName("SSA normalized end-pointed");
   return(0);
}
int deinit(){return(0);}

//+--------------------------------------------------------------------------------------+
//|                                                                                      |
//+--------------------------------------------------------------------------------------+
//
//
//
//
//

double trend[];
double slope[];

int start()
{
   int i,r,limit,counted_bars = IndicatorCounted();

      if(counted_bars < 0) return(-1);
      if(counted_bars > 0) counted_bars--;
         limit = MathMin(Bars-counted_bars,Bars-1);
         if (ArrayRange(trend,0)!=Bars)
            {
               ArrayResize(trend,Bars);
               ArrayResize(slope,Bars);
            }

   //
   //
   //
   //
   //
      
   if (MultiColor && slope[Bars-limit-1]==-1) CleanPoint(limit,inDa,inDb);
   for(i=limit, r=Bars-i-1; i>=0; i--,r++)
   {
      double ma    = iMA(NULL,0,SSAPeriodNormalization,0,MODE_SMA,SSAPrice,i);
      double dev   = iStdDev(NULL,0,SSAPeriodNormalization,0,MODE_SMA,SSAPrice,i)*3.0;
      double price = iMA(NULL,0,1,0,MODE_SMA,SSAPrice,i);
             no[i] = (price-ma)/(MathMax(dev,0.000001));
            
         //
         //
         //
         //
         //
 
         trend[r] = trend[r-1];
         slope[r] = slope[r-1];
         if (i<=FirstBar)
         {
            int ssaBars = MathMin(Bars-i,SSANumberOfBars);
            if (ssaBars<SSALag) continue;
               if (ArraySize(ssaIn) != ssaBars)
               {
                  ArrayResize(ssaIn ,ssaBars);
                  ArrayResize(ssaOut,ssaBars);
               }
               ArrayCopy(ssaIn,no,0,i,ssaBars);
 
            fastSingular(ssaIn,ssaBars,SSALag,SSANumberOfComputations,ssaOut);
            in[i]     = ssaOut[0];
            inDa[i]   = EMPTY_VALUE;
            inDb[i]   = EMPTY_VALUE;
            inDotu[i] = EMPTY_VALUE;
            inDotd[i] = EMPTY_VALUE;

            //
            //
            //
            //
            //
            
            if (in[i]> alertsLevel)                      trend[r] =  1;
            if (in[i]<-alertsLevel)                      trend[r] = -1;
            if (in[i]>-alertsLevel && in[i]<alertsLevel) trend[r] =  0;
            if (in[i]>in[i+1])                           slope[r] =  1;
            if (in[i]<in[i+1])                           slope[r] = -1;
            if (trend[r] != trend[r-1])
            {
               if (in[i]>0)
                  if (trend[r]==1)
                        inDotu[i] = alertsLevel;
                  else  inDotd[i] = alertsLevel;
               if (in[i]<0)
                  if (trend[r]==-1)
                        inDotd[i] = -alertsLevel;
                  else  inDotu[i] = -alertsLevel;
            }               
            if (MultiColor && slope[r]==-1) PlotPoint(i,inDa,inDb,in);
         }                   
   }

   //
   //
   //
   //
   //
   
   ArrayCopy(ssaCurrent,ssaOut);

   //
   //
   //
   //
   //

   if (alertsOn)
   {
      if (alertsOnCurrent)
           int whichBar = 0;
      else     whichBar = 1; whichBar = Bars-whichBar-1;
      if (trend[whichBar] != trend[whichBar-1])
      {
         if (trend[whichBar]   == 1)                        doAlert("level "+DoubleToStr( alertsLevel,2)+" crossed up");
         if (trend[whichBar]   ==-1)                        doAlert("level "+DoubleToStr(-alertsLevel,2)+" crossed down");
         if (trend[whichBar-1] == 1 && trend[whichBar]!= 1) doAlert("level "+DoubleToStr( alertsLevel,2)+" crossed down");
         if (trend[whichBar-1] ==-1 && trend[whichBar]!=-1) doAlert("level "+DoubleToStr(-alertsLevel,2)+" crossed up");
      }         
   }
   
   //
   //
   //
   //
   //
   
   SetIndexDrawBegin(0,Bars-ssaBars);
   return(0); 
}


//+--------------------------------------------------------------------------------------+
//|                                                                                      |
//+--------------------------------------------------------------------------------------+
//
//
//
//
//

void doAlert(string doWhat)
{
   static string   previousAlert="nothing";
   static datetime previousTime;
   string message;
   
      if (previousAlert != doWhat || previousTime != Time[0]) {
          previousAlert  = doWhat;
          previousTime   = Time[0];

          //
          //
          //
          //
          //

          message =  StringConcatenate(Symbol()," at ",TimeToStr(TimeLocal(),TIME_SECONDS)," normalized end-point SSA ",doWhat);
             if (alertsMessage) Alert(message);
             if (alertsEmail)   SendMail(StringConcatenate(Symbol()," normalized end-point SSA "),message);
             if (alertsSound)   PlaySound("alert2.wav");
      }
}

//+-------------------------------------------------------------------
//|                                                                  
//+-------------------------------------------------------------------
//
//
//
//
//

void CleanPoint(int i,double& first[],double& second[])
{
   if ((second[i]  != EMPTY_VALUE) && (second[i+1] != EMPTY_VALUE))
        second[i+1] = EMPTY_VALUE;
   else
      if ((first[i] != EMPTY_VALUE) && (first[i+1] != EMPTY_VALUE) && (first[i+2] == EMPTY_VALUE))
          first[i+1] = EMPTY_VALUE;
}

//
//
//
//
//

void PlotPoint(int i,double& first[],double& second[],double& from[])
{
   if (first[i+1] == EMPTY_VALUE)
      {
         if (first[i+2] == EMPTY_VALUE) {
                first[i]   = from[i];
                first[i+1] = from[i+1];
                second[i]  = EMPTY_VALUE;
            }
         else {
                second[i]   =  from[i];
                second[i+1] =  from[i+1];
                first[i]    = EMPTY_VALUE;
            }
      }
   else
      {
         first[i]  = from[i];
         second[i] = EMPTY_VALUE;
      }
}



I presume that you have the author's permission to use his stuff. We'll have to make a Zorro script from the mq4. It will be far shorter because all the MQL4 Indexbuffers, loops and other workarounds are not required in C. We'll do that step by step in the next days.

Attached Files
libSSA.zip (19 downloads)
Re: SSA implementation [Re: jcl] #417070
02/08/13 01:41
02/08/13 01:41
Joined: Nov 2012
Posts: 209
S
SFF Offline OP
Member
SFF  Offline OP
Member
S

Joined: Nov 2012
Posts: 209
So basically do we need to call that DLL in Zorro script?

Re: SSA implementation [Re: SFF] #417076
02/08/13 08:19
02/08/13 08:19
Joined: Jul 2000
Posts: 27,982
Frankfurt
jcl Offline

Chief Engineer
jcl  Offline

Chief Engineer

Joined: Jul 2000
Posts: 27,982
Frankfurt
Yes. For using a function inside a DLL, we define the function prototype and use the API macro:

Code:
void __stdcall fastSingular(double* sourceArray,int arraySize, int lag, int numberOfComputationLoops, double* destinationArray);
API(fastSingular,libSSA)



The libSSA.dll must be copied into the Zorro folder.

Next, we're going to reproduce what the EA does. All the complicated stuff with Indexbuffers, loops, ArrayCopy etc. is only required in MQL4, which does not natively support data series. It's not needed in a C script. The only needed part is the code where the data input to the SSA function is calculated:

Code:
double ma    = iMA(NULL,0,SSAPeriodNormalization,0,MODE_SMA,SSAPrice,i);
      double dev   = iStdDev(NULL,0,SSAPeriodNormalization,0,MODE_SMA,SSAPrice,i)*3.0;
      double price = iMA(NULL,0,1,0,MODE_SMA,SSAPrice,i);
             no[i] = (price-ma)/(MathMax(dev,0.000001));


no[i] is normalized data, calculated similar to Zorro's Fisher transform function from Workshop 5. The same code in lite-C:

Code:
vars close = series(priceClose());
var ma = SMA(close,SSAPeriodNormalization);
var dev = StdDev(close,SSAPeriodNormalization,1)*3.0;
vars no = series((close[0]-ma)/max(dev,0.000001));



You can now plot the normalized data for checking if it looks correct:

Code:
plot("Normalized",no[0],NEW,RED);



This is the input to the fastSingular function from the SSA DLL. Unfortunately I just found that the function does not work - it crashes when called. So, either the DLL has a bug, or more likely, fastSingular must be called with different parameters than I expected. It's sort of nontrivial to figure out the parameters of a function without having either the source code of the DLL or a proper documentation of it. But I'll experiment a little and think that I can find the correct parameters for getting the function not to crash.

Anyway, this is the basic way when you want to call functions from an external DLL in Zorro.

Re: SSA implementation [Re: jcl] #417084
02/08/13 09:58
02/08/13 09:58
Joined: Nov 2012
Posts: 209
S
SFF Offline OP
Member
SFF  Offline OP
Member
S

Joined: Nov 2012
Posts: 209
Great, thank you for your help.

Doesn't this code need in Zorro?

double price = iMA(NULL,0,1,0,MODE_SMA,SSAPrice,i);

Re: SSA implementation [Re: SFF] #417086
02/08/13 10:41
02/08/13 10:41
Joined: Jul 2000
Posts: 27,982
Frankfurt
jcl Offline

Chief Engineer
jcl  Offline

Chief Engineer

Joined: Jul 2000
Posts: 27,982
Frankfurt
No, because it's a MA with a 1-bar period, which is identical to the price itself. I have no idea why this line was required in the mq4 file, but programming in MQL4 is often a matter of trial and error. You find many strange things in mq4 EAs.

Re: SSA implementation [Re: jcl] #417107
02/08/13 13:37
02/08/13 13:37
Joined: Jul 2000
Posts: 27,982
Frankfurt
jcl Offline

Chief Engineer
jcl  Offline

Chief Engineer

Joined: Jul 2000
Posts: 27,982
Frankfurt
The reason of the crash was a size requirement for the output array.

Only one element of the fastSingular output array is used and it is unknown what that element really contains, as there is no documentation or source code. So it's up to you what you do with that SSA function. From the original EA above I assume that the output contains the basic trend of the price series. The alerts are triggered by that signal.

Anyway here's the complete code for the SSA implementation:
Code:
#define SSALag			25
#define SSANumberOfComputations	2
#define SSAPeriodNormalization	10
#define SSANumberOfBars 	300

void __stdcall fastSingular(double* sourceArray,int arraySize, int lag, int numberOfComputationLoops, double* destinationArray);
API(fastSingular,libSSA)

// helper function to reverse a series
vars reverse(vars Data)
{
	vars Reversed = series();
	int i;
	for(i=0; i<LookBack; i++)
		Reversed[i] = Data[LookBack-i-1];
	return Reversed;
}


function run()
{
	LookBack = SSANumberOfBars;
	StartDate = 20121001;
	EndDate = 20130101;

	vars close = series(priceClose());
	var ma = SMA(close,SSAPeriodNormalization);
	var dev = StdDev(close,SSAPeriodNormalization,1);
	vars no = series((close[0]-ma)/max(dev,0.000001));
	
	var out[SSANumberOfBars];
	fastSingular(reverse(no),SSANumberOfBars,SSALag,SSANumberOfComputations,out);
	
	plot("Normalized",no[0],NEW,RED);
	plot("SSA",out[0],AXIS2,BLUE);
	set(PLOTPRICE+PLOTNOW);
}



The fastSingular function expects the series in reverse order, that's why we need the additional "reverse" function for reversing a series. The blue line in the chart is the SSA output.

Re: SSA implementation [Re: jcl] #417146
02/08/13 23:30
02/08/13 23:30
Joined: Nov 2012
Posts: 209
S
SFF Offline OP
Member
SFF  Offline OP
Member
S

Joined: Nov 2012
Posts: 209
Thank you very much for writing the full code for me.
It will be useful for someone.

By the way I got error in my old Zorro.

'EndDate' undeclared identifier

Is it because of the old version?

Updata:

I just copied and pasted your code above but I got another error.

SSA run..
Undefined function called!
Generate Chart - please wait... ok

Updata 2:
I got the code working by editing the code like below.

var __stdcall

Is it possible to color the line in plot?

The original script is just 1 color line but Zorro version is not.
Could you please make the exact same line?
Also, The line of 0.25 and -0.25 needed.

Last edited by SFF; 02/09/13 00:12.
Re: SSA implementation [Re: SFF] #417177
02/09/13 17:07
02/09/13 17:07
Joined: Apr 2008
Posts: 586
Austria
Petra Online
Support
Petra  Online
Support

Joined: Apr 2008
Posts: 586
Austria
The color of lines is the last parameter in the plot call, use something different than "BLUE", and a black line at -0.25 goes like this: plot("Line",-0.25,0,BLACK). Hope I could help.

Re: SSA implementation [Re: Petra] #417213
02/10/13 00:11
02/10/13 00:11
Joined: Nov 2012
Posts: 209
S
SFF Offline OP
Member
SFF  Offline OP
Member
S

Joined: Nov 2012
Posts: 209
There is 2 color in the one line. How to define it?

Re: SSA implementation [Re: SFF] #417274
02/11/13 09:17
02/11/13 09:17
Joined: Jul 2000
Posts: 27,982
Frankfurt
jcl Offline

Chief Engineer
jcl  Offline

Chief Engineer

Joined: Jul 2000
Posts: 27,982
Frankfurt
Define what?

Re: SSA implementation [Re: jcl] #417286
02/11/13 10:28
02/11/13 10:28
Joined: Nov 2012
Posts: 209
S
SFF Offline OP
Member
SFF  Offline OP
Member
S

Joined: Nov 2012
Posts: 209
2 color line of SSA as the original.

Re: SSA implementation [Re: SFF] #417289
02/11/13 10:44
02/11/13 10:44
Joined: Jul 2000
Posts: 27,982
Frankfurt
jcl Offline

Chief Engineer
jcl  Offline

Chief Engineer

Joined: Jul 2000
Posts: 27,982
Frankfurt
A line can have only one color, so for two colors plot two alternating lines. The color is however irrelevant for trading.

Re: SSA implementation [Re: jcl] #418103
02/21/13 09:53
02/21/13 09:53
Joined: Nov 2012
Posts: 209
S
SFF Offline OP
Member
SFF  Offline OP
Member
S

Joined: Nov 2012
Posts: 209
Another SSA version converted.
Please check if it is identical to the original code.

The Zorro code.
Code:
#define SSALag			25
#define SSANumberOfComputations	2
#define SSANumberOfBars 	300

var __stdcall fastSingular(double* sourceArray,int arraySize, int lag,int numberOfComputationLoops, double* destinationArray);
API(fastSingular,libSSA)

// helper function to reverse a series
vars reverse(vars Data)
{
	vars Reversed = series();
	int i;
	for(i=0; i<LookBack; i++)
		Reversed[i] = Data[LookBack-i-1];
	return Reversed;
}


function run()
{
	LookBack = SSANumberOfBars;
	BarPeriod = 15;
LookBack = 500;
StartDate = 20121031;
NumDays = 1;
	

	vars close = series(priceClose());

	
	var out[SSANumberOfBars];
	fastSingular(reverse(close),SSANumberOfBars,SSANumberOfComputations,SSALag,out);
	
	plot("Normalized",close[0],0,RED);
	set(PLOTPRICE+PLOTNOW);
}



The original code.
Code:
//+--------------------------------------------------------------------------------------+
//|                                                                                      |
//+--------------------------------------------------------------------------------------+
#property copyright "www.forex-tsd.com"
#property link      "www.forex-tsd.com"

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 PaleVioletRed
#property indicator_width1 2

#import "libSSA.dll"
   void fastSingular(double& sourceArray[],int arraySize, int lag, int numberOfComputationLoops, double& destinationArray[]);
#import

//
//
//
//
//

extern int SSAPrice                =  PRICE_CLOSE;
extern int SSALag                  =  25;
extern int SSANumberOfComputations =   2;
extern int SSANumberOfBars         = 300;
extern int FirstBar                = 300; 

//
//
//
//
//

double in[];
double pr[];
double ssaIn[];
double ssaOut[];

//+--------------------------------------------------------------------------------------+
//|                                                                                      |
//+--------------------------------------------------------------------------------------+
//
//
//
//
//

int init()
{
   IndicatorBuffers(2);
      SetIndexBuffer(0,in);
      SetIndexBuffer(1,pr);
   IndicatorShortName("SSA end-pointed");
   return(0);
}
int deinit(){return(0);}

//+--------------------------------------------------------------------------------------+
//|                                                                                      |
//+--------------------------------------------------------------------------------------+
//
//
//
//
//

int start()
{
   int i,limit,counted_bars = IndicatorCounted();

   if(counted_bars < 0) return(-1);
   if(counted_bars > 0) counted_bars--;
      limit = MathMin(Bars-counted_bars,Bars-1);

   //
   //
   //
   //
   //

   for(i=limit; i>=0; i--)
   {
      pr[i] = iMA(NULL,0,1,0,MODE_SMA,SSAPrice,i);

      //
      //
      //
      //
      //
      
      if (i<=FirstBar)
      {
         int ssaBars = MathMin(Bars-i,SSANumberOfBars);
         if (ssaBars<SSALag) continue;
               if (ArraySize(ssaIn) != ssaBars)
               {
                  ArrayResize(ssaIn ,ssaBars);
                  ArrayResize(ssaOut,ssaBars);
               }
               ArrayCopy(ssaIn,pr,0,i,ssaBars);
      
         fastSingular(ssaIn,ssaBars,SSALag,SSANumberOfComputations,ssaOut);
         in[i]=ssaOut[0];
      }                   
   }                  
   return(0); 
}


Last edited by SFF; 02/21/13 10:08.
Re: SSA implementation [Re: SFF] #418105
02/21/13 10:05
02/21/13 10:05
Joined: Jul 2000
Posts: 27,982
Frankfurt
jcl Offline

Chief Engineer
jcl  Offline

Chief Engineer

Joined: Jul 2000
Posts: 27,982
Frankfurt
The lag is different. But from what I understood, fastSingular needs normalized input data - so how can that code work?

Re: SSA implementation [Re: jcl] #418107
02/21/13 10:08
02/21/13 10:08
Joined: Nov 2012
Posts: 209
S
SFF Offline OP
Member
SFF  Offline OP
Member
S

Joined: Nov 2012
Posts: 209
Hi,

It seems same plot that if I have put normalized input data.
I notice that my code just plots a MA of priceClose().

By the way, is it possible to use an up/down arrow in plot?

Last edited by SFF; 02/22/13 04:23.
Re: SSA implementation [Re: SFF] #418218
02/22/13 09:49
02/22/13 09:49
Joined: Nov 2012
Posts: 209
S
SFF Offline OP
Member
SFF  Offline OP
Member
S

Joined: Nov 2012
Posts: 209
Could you post a complete correct code for it?
I only plot just MA not SSA line.

Re: SSA implementation [Re: SFF] #476793
04/03/19 22:27
04/03/19 22:27
Joined: Apr 2019
Posts: 1
X
xeim Offline
Guest
xeim  Offline
Guest
X

Joined: Apr 2019
Posts: 1
Hi guys,

How do I get a source code of libssa.dll?
I want to compile it as 64-bit library.

Re: SSA implementation [Re: xeim] #476805
04/04/19 12:54
04/04/19 12:54
Joined: Feb 2017
Posts: 1,725
Chicago
AndrewAMD Online
Serious User
AndrewAMD  Online
Serious User

Joined: Feb 2017
Posts: 1,725
Chicago
Originally Posted By: xeim
Hi guys,

How do I get a source code of libssa.dll?
I want to compile it as 64-bit library.
Step 1: find the author of libssa.dll.
Step 2: Kindly ask the author to supply source.
Step 3: If author refuses, kindly ask the author to recompile in 64-bit.
Step 4: If that fails, you are S.O.L. (Unless you know how decompilers work. cool )

Page 1 of 2 1 2

Moderated by  Petra 

Gamestudio download | chip programmers | Zorro platform | shop | Data Protection Policy

oP group Germany GmbH | Birkenstr. 25-27 | 63549 Ronneburg / Germany | info (at) opgroup.de

Powered by UBB.threads™ PHP Forum Software 7.7.1