Thread Swarm with leader and global variables , for 1 terminal

Thread Swarm with leader and global variables , for 1 terminal

13 December 2023, 14:14
Lorentzos Roussos
0
110

Let's assume you have data that must be downloaded on a server .

This data concerns multiple symbols not just one and you are not making one request per symbol separately.

You need to be running multiple "stuff" (indicator/ea) in one terminal but you don't want all of them to try and perform this one unique but similar task (whatever it is) at the same time . You want one of them to go fetch the data and inform everyone else to open it.

So , we need a swarm .

We will call swarm the set of threads (indicators/eas) that are running the code and each and every one of them a swarm unit.

This swarm will be active for as long as there is at least one swarm unit still active (i.e. an open chart)

Let's assume 3 datetime variables :

  • Last check for data : This is the last time "anything" from this terminal asked the server the question "is there new data?"
  • Last data time : This is the last time "anyone" from this terminal downloaded new data.
  • Last update time : This is the time of the "LastDataTime" this swarm unit is running with.

These are used directly for all the checks we need , and these are the checks regarding the data.

  • When was the last time we checked , so that we don't flood the server.
  • When was the last time we downloaded.
  • The data this swarm unit has , when was it downloaded. 

Also you will need to indentify whether or not the data you are getting is the same as the data you already have , but you do that in your server or in your files , or whatever constitutes the "external" thing you want to work with.

So let's dive right in :

First the code for one swarm unit

class swarm_unit{
         public:
    long chart_id;//the chart id 
    long leader_id;//the leader id
    long others[];//ids of other open charts
datetime last_check_for_data; 
datetime last_data_time;
datetime last_update_time;

The chart id is the id of the chart the unit is running on .

Then we need a list of all the other units in the swarm that are still active.

And lastly we need to know who the leader is , again a chart id.

then the 3 times for checking and interacting with the "external" packages as mentioned above

Time to start deploying our functions .

What are we going to need ? A way to find the other swarm units that are still active.

  • We will loop through the open charts 
  • We will look for an ea that has a swarm unit 
  • If we don't find it we will search for an indicator with a swarm unit
  • we will collect the ids of the charts in the others[] array
   void get_others_from_charts(){
        ArrayFree(others);
        long read_chart=ChartFirst();
        while(read_chart>=0){
               bool has_swarm_unit=false;//if this chart has a swarm unit 
             //if not this chart 
               if(read_chart!=ChartID()&&read_chart!=0){
               //look for ea
                 if(ChartGetString(read_chart,CHART_EXPERT_NAME)==SWARM_EA_NAME){
                   has_swarm_unit=true;
                   }
                 else{
               //look for indicator
                 //get windows 
                   int windows=(int)ChartGetInteger(read_chart,CHART_WINDOWS_TOTAL);
                   //loop to windows of that chart
                     for(int w=0;w<windows;w++){
                        //indicators on that window 
                          int indicators=ChartIndicatorsTotal(read_chart,w);
                          //loop to indicators 
                            for(int i=0;i<indicators;i++){
                               string indicator_name=ChartIndicatorName(read_chart,w,i);
                               if(indicator_name==SWARM_INDY_SHORTNAME){
                                 has_swarm_unit=true;
                                 break;
                                 }
                               }
                          //loop to indicators ends here 
                          if(has_swarm_unit){break;}
                        }
                   }
                   //look for an indicator ends here
                   //loop to windows of that chart ends here 
                   if(has_swarm_unit){
                     add_to_others(read_chart);
                     }
                 }
               //if not this chart ends here
             if(read_chart!=0){
               read_chart=ChartNext(read_chart);
               }else{
               read_chart=ChartNext(ChartID());
               }
             }
        }

So our unit knows itself , knows the IDs of the other units now it needs to know who the leader is.

To facilitate these checks we will deploy a small "bridge" using global variables.

We need 4 things :

  • To be able to lock and unlock the bridge
  • To store the id of the leader 
  • To store the last check time 
  • To store the last data time

The lock works as follows :

If the bridge is not locked lock it and assign the chart id of the swarm unit that locked it to it.

If the bridge is locked :

    • If the swarm unit that locked the bridge is no longer active , lock the bridge
    • If the swarm unit that locked the bridge is active do nothing

/* lock unlock systems for GV */
//check if common global swarm variable space is locked
bool canOccupyGSVS(swarm_unit &self,bool &reLock){
  reLock=true;//this means , write new lock
//check lock global variable 
  long gv=GvToLong(GVSS_LOCK);
  //this is the id of the chart that locked it essentially
  if(gv>0){
  if(gv==ChartID()){reLock=false;return(true);}//if self
  else{
   reLock=false;
   //so is the chart that locked the swarm open ?
   for(int i=0;i<ArraySize(self.others);i++){
      if(self.others[i]==gv){
        return(false);
        }
      }
   //if we reach this point it means the chart that locked the swarm is gone
     reLock=true;//so relock
     return(true);//and we can occupy!
   }
  }
return(true);
} 

void UnlockGVSS(){
LongToGV(GVSS_LOCK,0);
}


Above is the check lock function

We pass the swarm unit (of this chart) and a reference bool for whether or not we can relock the bridge.

This function will return true if we can go on interacting with the bridge from this swarm unit , and also an additional true/false if the bridge must be locked.

  1. it pulls the id of the locking unit
  2. if there is no id we can interact and we must relock
  3. if there is an id and it is actually of this chart , we can proceed but we don't relock
  4. if we find the id in the others[] we don't proceed and we don't relock
  5. if we dont find the id in the others[] we can proceed and we can relock

And we package all the above in one swarm maintainance function :

    void swarm_maintain(){
         get_others_from_charts();
         //is the Swarm system occupied ? 
          bool reLock=false;
          if(canOccupyGSVS(this,reLock)){
          //relock if needed
            if(reLock){
              LongToGV(GVSS_LOCK,chart_id);
              }
          //is there a leader ? 
            long new_leader=GvToLong(GVSS_LEADER);
          //if there is no leader , become it
            if(new_leader==0){
                leader_id=chart_id;
                LongToGV(GVSS_LEADER,chart_id);
              }
          //if there is a leader 
            else{
              bool is_on=false;
              for(int i=0;i<ArraySize(others);i++){if(others[i]==new_leader){is_on=true;break;}}
              //if leader is active
                if(is_on){
                leader_id=new_leader;
                }
              //if leader closed
                else{
                //become the leader 
                  leader_id=chart_id;
                  LongToGV(GVSS_LEADER,chart_id);
                }
              } 
          //if this chart is leading 
            if(chart_id==leader_id){
            //acquire last check time 
              last_check_for_data=(datetime)GvToLong(GVSS_LAST_CHECK_FOR_DATA_TIME);
              long elapsed=TimeGMT()-last_check_for_data;
              if(elapsed>=SecsForNewAnalysisCheck){
                //im placing a bool check here 
                  bool gotnew=false;
                //if you get new data update the last_data_time
                  if(gotnew){
                  last_data_time=last_check_for_data;
                  //we also write to the GVs to inform the other units theres new data
                  LongToGV(GVSS_LAST_DATA_TIME,((long)last_data_time));
                  }
                }
            }
            //we need to know if there is new data to load
              last_data_time=(datetime)GvToLong(GVSS_LAST_DATA_TIME);
              if(last_data_time>last_update_time){
                //load the fetched data here 
                //and update the update time
                  last_update_time=last_data_time;
                }

          UnlockGVSS();
          }
        //is the Swarm system occupied ? ends here 
        }

  1. we get a list of all the other active swarm units
  2. we check if we can interact with the bridge

If we can interact with the bridge :

  1. we check if there is a leader
  2. if there is no leader this unit becomes the leader
  3. if there is a leader we check whether or not it is still active.
  4. if it is not active this unit becomes the leader

If there is a leader and is active

  1. if this unit is the leader it manages interaction with the external data source
  2. if this unit is not the leader it is checking the latest data time in order to decide if it must load new data that the leader received

And this is it more or less 

I'm attaching the test code for an indicator 

If you find any issues let me know

#property indicator_chart_window
#define SWARM_INDY_SHORTNAME "SwarmTest"
#define SWARM_EA_NAME "SwarmTest"
#define GVSS_NAME "SWARMGV"
#define GVSS_LOCK GVSS_NAME+"_LOCK"
#define GVSS_LEADER GVSS_NAME+"_LEADER"
#define GVSS_LAST_CHECK_FOR_DATA_TIME GVSS_NAME+"_LAST_CHECK"
#define GVSS_LAST_DATA_TIME GVSS_NAME+"_LAST_TIME"
input long SecsForNewAnalysisCheck=60;//Seconds interval for checking for new data
//you should place this inside your data structures or smth , here is by itself:
int OnInit()
  {   
  SWU.reset(true,SWARM_INDY_SHORTNAME);
  SWU.setup(ChartID());
  EventSetMillisecondTimer(1000);       
  return(INIT_SUCCEEDED);
  }
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[])
  {
  return(rates_total);
  }
void OnTimer()
  {
  SWU.swarm_maintain();
  SWU.display_swarm();
  }


/* SWARM 
*/
class swarm_unit{
         public:
    long chart_id;//the chart id 
    long leader_id;//the leader id
    long others[];//ids of other open charts
datetime last_check_for_data; 
datetime last_data_time;
datetime last_update_time;
         swarm_unit(void){reset();}
        ~swarm_unit(void){reset();}
    void reset(bool is_indicator=false,string indyshortname=NULL){
         last_check_for_data=0;
         chart_id=-1;
         leader_id=-1;
         ArrayFree(others);
         if(is_indicator){
           IndicatorSetString(INDICATOR_SHORTNAME,indyshortname);
           }
         }
    void setup(long _id){
         chart_id=_id;
         }
    void swarm_maintain(){
         get_others_from_charts();
         //is the Swarm system occupied ? 
          bool reLock=false;
          if(canOccupyGSVS(this,reLock)){
          //relock if needed
            if(reLock){
              LongToGV(GVSS_LOCK,chart_id);
              }
          //is there a leader ? 
            long new_leader=GvToLong(GVSS_LEADER);
          //if there is no leader , become it
            if(new_leader==0){
                leader_id=chart_id;
                LongToGV(GVSS_LEADER,chart_id);
              }
          //if there is a leader 
            else{
              bool is_on=false;
              for(int i=0;i<ArraySize(others);i++){if(others[i]==new_leader){is_on=true;break;}}
              //if leader is active
                if(is_on){
                leader_id=new_leader;
                }
              //if leader closed
                else{
                //become the leader 
                  leader_id=chart_id;
                  LongToGV(GVSS_LEADER,chart_id);
                }
              } 
          //if this chart is leading 
            if(chart_id==leader_id){
            //acquire last check time 
              last_check_for_data=(datetime)GvToLong(GVSS_LAST_CHECK_FOR_DATA_TIME);
              long elapsed=TimeGMT()-last_check_for_data;
              if(elapsed>=SecsForNewAnalysisCheck){
                //im placing a bool check here 
                  bool gotnew=false;
                //if you get new data update the last_data_time
                  if(gotnew){
                  last_data_time=last_check_for_data;
                  //we also write to the GVs to inform the other units theres new data
                  LongToGV(GVSS_LAST_DATA_TIME,((long)last_data_time));
                  }
                }
            }
            //we need to know if there is new data to load
              last_data_time=(datetime)GvToLong(GVSS_LAST_DATA_TIME);
              if(last_data_time>last_update_time){
                //load the fetched data here 
                //and update the update time
                  last_update_time=last_data_time;
                }

          UnlockGVSS();
          }
        //is the Swarm system occupied ? ends here 
        }
   void get_others_from_charts(){
        ArrayFree(others);
        long read_chart=ChartFirst();
        while(read_chart>=0){
               bool has_swarm_unit=false;//if this chart has a swarm unit 
             //if not this chart 
               if(read_chart!=ChartID()&&read_chart!=0){
               //look for ea
                 if(ChartGetString(read_chart,CHART_EXPERT_NAME)==SWARM_EA_NAME){
                   has_swarm_unit=true;
                   }
                 else{
               //look for indicator
                 //get windows 
                   int windows=(int)ChartGetInteger(read_chart,CHART_WINDOWS_TOTAL);
                   //loop to windows of that chart
                     for(int w=0;w<windows;w++){
                        //indicators on that window 
                          int indicators=ChartIndicatorsTotal(read_chart,w);
                          //loop to indicators 
                            for(int i=0;i<indicators;i++){
                               string indicator_name=ChartIndicatorName(read_chart,w,i);
                               if(indicator_name==SWARM_INDY_SHORTNAME){
                                 has_swarm_unit=true;
                                 break;
                                 }
                               }
                          //loop to indicators ends here 
                          if(has_swarm_unit){break;}
                        }
                   }
                   //look for an indicator ends here
                   //loop to windows of that chart ends here 
                   if(has_swarm_unit){
                     add_to_others(read_chart);
                     }
                 }
               //if not this chart ends here
             if(read_chart!=0){
               read_chart=ChartNext(read_chart);
               }else{
               read_chart=ChartNext(ChartID());
               }
             }
        }
   void display_swarm(){
        string swarmids="[SELF] "+IntegerToString(chart_id);
        if(leader_id==chart_id){swarmids+=" <LEADER>";}
        swarmids+="\n";
        for(int i=0;i<ArraySize(others);i++){
           swarmids+="["+IntegerToString(i)+"] "+IntegerToString(others[i]);
           if(others[i]==leader_id){
             swarmids+=" <LEADER>";
             }
           swarmids+="\n";
           }
        Comment(swarmids);
        }
 string bool_to_string(bool _bool){if(_bool){return("True");}return("False");}
        private:
   void add_to_others(long id){
        ArrayResize(others,ArraySize(others)+1,0);
        others[ArraySize(others)-1]=id;
        }
};

swarm_unit SWU;

/* lock unlock systems for GV */
//check if common global swarm variable space is locked
bool canOccupyGSVS(swarm_unit &self,bool &reLock){
  reLock=true;//this means , write new lock
//check lock global variable 
  long gv=GvToLong(GVSS_LOCK);
  //this is the id of the chart that locked it essentially
  if(gv>0){
  if(gv==ChartID()){reLock=false;return(true);}//if self
  else{
   reLock=false;
   //so is the chart that locked the swarm open ?
   for(int i=0;i<ArraySize(self.others);i++){
      if(self.others[i]==gv){
        return(false);
        }
      }
   //if we reach this point it means the chart that locked the swarm is gone
     reLock=true;//so relock
     return(true);//and we can occupy!
   }
  }
return(true);
} 

void UnlockGVSS(){
LongToGV(GVSS_LOCK,0);
}


long GvToLong(string name){
if(GlobalVariableCheck(name+"_A")&&GlobalVariableCheck(name+"_B")&&GlobalVariableCheck(name+"_AFrontDigits")&&GlobalVariableCheck(name+"_BFrontDigits")&&GlobalVariableCheck(name+"_ABackDigits")&&GlobalVariableCheck(name+"_BBackDigits")){
  int digits_front_a=(int)MathFloor(GlobalVariableGet(name+"_AFrontDigits"));
  int digits_back_a=(int)MathFloor(GlobalVariableGet(name+"_ABackDigits"));
  int digits_front_b=(int)MathFloor(GlobalVariableGet(name+"_BFrontDigits"));
  int digits_back_b=(int)MathFloor(GlobalVariableGet(name+"_BBackDigits"));
  long p1=(long)MathFloor(GlobalVariableGet(name+"_A"));
  long p2=(long)MathFloor(GlobalVariableGet(name+"_B"));
  return(assembleParts(p1,p2,digits_front_a,digits_back_a,digits_front_b,digits_back_b));
  }
return(0);
}

void LongToGV(string name,long value){
long part_a=0,part_b=0;
 int part_a_front_digits=0,part_a_back_digits=0,part_b_front_digits=0,part_b_back_digits=0;
 long_to_parts(value,part_a,part_a_front_digits,part_a_back_digits,part_b,part_b_front_digits,part_b_back_digits); 
 GlobalVariableSet(name+"_A",part_a);
 GlobalVariableSet(name+"_B",part_b);
 GlobalVariableSet(name+"_AFrontDigits",part_a_front_digits);
 GlobalVariableSet(name+"_ABackDigits",part_a_back_digits);
 GlobalVariableSet(name+"_BFrontDigits",part_b_front_digits);
 GlobalVariableSet(name+"_BBackDigits",part_b_back_digits);
}

long assembleParts(long part_a,
                   long part_b,
                    int part_a_front_digits,
                    int part_a_back_digits,
                    int part_b_front_digits,
                    int part_b_back_digits){
string partA=longToStringWithDigits(part_a,part_a_front_digits,part_a_back_digits);
string partB=longToStringWithDigits(part_b,part_b_front_digits,part_b_back_digits);
string full=partA+partB;
long result=(long)StringToInteger(full);
return(result);
}

string longToStringWithDigits(long l,int front_digits,int back_digits){
string front="";
for(int i=0;i<front_digits;i++){
   front+="0"; 
   }
string back="";
for(int i=0;i<back_digits;i++){
   back+="0";
   }
string middle=IntegerToString(l);
return(front+middle+back);
}

void long_to_parts(long original,
                   long &part_a,
                    int &part_a_front_digits,
                    int &part_a_back_digits,
                   long &part_b,
                    int &part_b_front_digits,
                    int &part_b_back_digits){
string full=IntegerToString(original);
  part_a=0;
  part_a_front_digits=0;
  part_a_back_digits=0;//this may be useless but roll with it now
  part_b=0;
  part_b_front_digits=0;
  part_b_back_digits=0;
//count digits
  int alldigits=StringLen(full);
//split
  int alength=(int)MathFloor(((double)alldigits)/((double)2.0));
  int blength=alldigits-alength;
  string partA=StringSubstr(full,0,alength);
  string partB=StringSubstr(full,alength,blength);
//---
  /*
  okay so what is the fuss about ?
  lets take this long number 12345600002446231000
  length is 20
  kay
  so part a is 10 digits 
  and part b is 10 digits
  okay but 
  when we store it in GV the leading zeroes may get evaporated
  and we dont want that so , scan for 0
  */
  //part a 
    for(int i=0;i<alength;i++){
    string ch=StringSubstr(partA,i,1);
    if(ch=="0"){part_a_front_digits++;}
    else{break;}
    }
    for(int i=alength-1;i>=0;i--){
    string ch=StringSubstr(partA,i,1);
    if(ch=="0"){part_a_back_digits++;}
    else{break;}
    }
  //part b
    for(int i=0;i<blength;i++){
    string ch=StringSubstr(partB,i,1);
    if(ch=="0"){part_b_front_digits++;}
    else{break;}
    }
    for(int i=blength-1;i>=0;i--){
    string ch=StringSubstr(partB,i,1);
    if(ch=="0"){part_b_back_digits++;}
    else{break;}
    }   
part_a=(long)StringToInteger(partA);
part_b=(long)StringToInteger(partB); 
}


void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam){

}

void OnDeinit(const int reason)
{

}





Share it with friends: