Mql5自定义品种 中国金融产品: A股票、期货、债券、数字货币的应用(3)------个股日线复权

 

5、A股 除权 向前复权 向后复权处理过程

股票复权是个难点,特别是向后复权,Tushare的python数据请求可以直接访问复权数据,其计算有服务端API自动完成。但是Http访问Tushare只提供复权因子需要自行计算复权。向后复权不同平台处理过程不一样,中国一些常见的股票平台 如同花顺和通达信都简化或去除了向后复权。本文用300015.SZ在2019年6月4日~6月5日除权 复权段做实例,将向前复权和向后复权的数据过程展现。

通过向后复权计算 可以看到300015.SZ爱尔眼科复权后价格在800~1000元,如果做算法和量化分析想基于全时域分析而不是某特定时段和周期区间一定要用复权数据特别是向后复权,否则对于股票这样的“准连续、随机过程”数据进行算法分析失真。因此建议学习量化构建EA最好先基于外汇货币对在Mql5系统进行指标和EA的训练之后再转向中国A股,而不是一开始就针对A股这样的复杂  随机过程的时间序列作为目标分析。

//------------------------读取个股数据----download stock data-------------------------------------
void ReadStock(const string  &token, const string &stockName,const datetime &tBegin,const datetime &tEnd)
{
       //........省略代码

                      
                      MqlRates Rates[];
                      
                      Tushare_CharArrayToStruct(result,Rates);//---将HTTP POST返回char数组拆分到MQL自定义品种结构中--
                      
                      Adj_Process(Token,stockName,Adj,Rates);//------除权、复权过程-----
                      
                      CustomSymbolCreate(stockName, "\\Astock");//----自定义Astock品类目录----
                      
                      int reg=CustomRatesReplace(stockName,tBegin,tEnd,Rates,WHOLE_ARRAY );//----将MqlRates数组数据建立并导入自定义品类目录下的产品中
                         
                      MessageBox(stockName+" "+"Downloaded"+" "+Freq+" "+"OK!" );
                     
                      
                   //}
                   //else
                   //{
                   //    Print("Error in FileOpen. Error code =", GetLastError());
                   //}
                   
               }
               else
                   PrintFormat("Error in download '%s', code %d", END_POINT_URL, res);
           }
           
}


//-----------------------------------------------------------------------------------------------------------------
//----------------------------------将HTTP POST返回char数组拆分到MQL自定义品种结构中---------------------------------------------

 void Tushare_CharArrayToStruct(const char &result[],MqlRates &Rates[])
 {
      //---------------------将Tushare返回的char数组合并成字符串处理过程--------------------------------------
      string str=CharArrayToString(result,0,-1,0);  
      //Alert(str); 
      string str2=StringSubstr(str,228);
         
      int rc=StringReplace(str2,"\"","");
      rc+=StringReplace(str2,"[","");
      rc+=StringReplace(str2,"]","\r\n");
      //Alert(str2); 
      
      //----------------------将处理好的字符串分装到周期字符串数组--------------------------------------------------
      string sep="\r\n";                            // 分隔符为字符 
      ushort u_sep;                                 // 分隔符字符代码 
      string trade_Arr[];                           // 获得字符串数组 
      //--- 获得分隔符代码 
      u_sep=StringGetCharacter(sep,0); 
      //--- 字符串分为子字符串 
      int k=StringSplit(str2,u_sep,trade_Arr);     //---用分隔符切分周期字符串并装入数组
      trade_Arr[0]=","+trade_Arr[0];
    
     //----------------进一步处理周期字符串数组装入MqlRates结构数组中--------------------------------------------------------------------------------
      //MessageBox(trade_Arr[2]);
      
      k=k-2;
      ArrayResize(Rates,k);
      
      for(int i=0;i<k;i++)
      {
        string su_Arr[];
        sep=",";
        u_sep=StringGetCharacter(sep,0);
        int m=StringSplit(trade_Arr[i],u_sep,su_Arr); 
        
        //MessageBox(trade_Arr[i]);

        //按照MQL5历史数据格式重新整合数组格式
        //trade_Arr[i]=su_Arr[2]+"  "+"09:30:00"+"  "+su_Arr[3]+"   "+su_Arr[4]+"  "+su_Arr[5]+"  "+su_Arr[6]+"  "+"0"+"  "+su_Arr[10]+" "+"0"+"\r\n";

        
        
        datetime  stm=D'1980.07.19 12:30:27';
        
        stm=StringToTime(su_Arr[2]+" "+"09:30:01");
     
        //ArraySetAsSeries(Rates,false);
        Rates[i].time=stm;
        //Rates[i].time=StringToTime("09:30:00");
        Rates[i].open=StringToDouble(su_Arr[3]);
        Rates[i].high=StringToDouble(su_Arr[4]);
        Rates[i].low=StringToDouble(su_Arr[5]);
        Rates[i].close=StringToDouble(su_Arr[6]);
        Rates[i].tick_volume=StringToDouble(su_Arr[10]);
       
        Rates[i].real_volume=StringToDouble(su_Arr[11]);
        
        //MessageBox(Rates[i].time+","+Rates[i].close+","+Rates[i].real_volume);
   }
   //MessageBox(TimeToString( Rates[0].open));
  //--------------------------------------------------------------------------------------------------------------------------------------------------------
  
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------
//------------------------------------读取复权因子和复权过程-------------------------------------        
void Adj_Process(const string  &token, const string &stockName,const string &adj,MqlRates &Rates[])
{

            char     message_data[], result[];
           
            string   RequestMethod     ="POST";
            string   END_POINT_URL     ="http://api.waditu.com";
            string   headers           = "content-type:application/json"+"\r\n";
                     headers           += ",charset=UTF-8" + "\r\n";
            int      timeout           =  10000;
            string   result_headers    =  NULL;
            string   message_text     ;

                     
           //--------------------将字符整合成Post接受的Json标准的char 字符串---------           
           StringConcatenate(message_text,
                              "{",
                              "\"api_name\"",
                              ":",
                              "\"adj_factor\"", 
                              ",",
                              
                              
                              "\"token\"",
                              ":",
                              "\"",
                              token,
                              "\"",
                              ",",
         
                              
                              "\"params\"",
                              ":",
                              "{",
                              
                              "\"ts_code\"",
                              ":",
                              "\"",
                              StockName,
                              "\"",
                              ",",
                              
                              "\"trade_date\"",
                              ":",
                              "\"\"",
                              "}"
                              ",",
                              
                              "\"fields\"",
                              ":",
                              "\"\"",
                              "}"
                            );

         
           StringToCharArray(message_text,message_data,0, StringLen(message_text));
           
           string _jt=CharArrayToString(message_data);
           
           //Alert(_jt); 
           //MessageBox(_jt);//---------------------------------------------------------------------------------
           //--------------------------------------------------------------------------------------------------------------
           //------------------------------------------Http Post 请求服务器下载数据----------------------------------------
           ResetLastError();
         
           int res =WebRequest(RequestMethod, END_POINT_URL, headers, timeout, message_data, result, result_headers);
         
           if (res == -1)
           {
                Print("Error in WebRequest. Error code  =",GetLastError());
               //--- Perhaps the URL is not listed, display a message about the necessity to add the address
                MessageBox("Add the address '"+END_POINT_URL+"' to the list of allowed URLs on tab 'Expert Advisors'","Error",MB_ICONINFORMATION); 
           }
           else
           {
               if (res == 200)
               {

               struct Mqladj
               {
               datetime time;
               double adj_data;
               };
               
               
               Mqladj Adj_arr[];
               
               string str=CharArrayToString(result,159,-1,0);  
               //Alert(str); 
              
                  
               int rc=StringReplace(str,"\"","");
               rc+=StringReplace(str,"[","");
               rc+=StringReplace(str,"]","\r\n");
               //Alert(str); 
               
               //----------------------将处理好的字符串分装到周期字符串数组--------------------------------------------------
               string sep="\r\n";                            // 分隔符为字符 
               ushort u_sep;                                 // 分隔符字符代码 
               string trade_Arr[];                           // 获得字符串数组 
               //--- 获得分隔符代码 
               u_sep=StringGetCharacter(sep,0); 
               //--- 字符串分为子字符串 
               int k=StringSplit(str,u_sep,trade_Arr);     //---用分隔符切分周期字符串并装入数组
               trade_Arr[0]=","+trade_Arr[0];
               
               //---------------------------------------------------------------------------------
               //----------------进一步处理周期字符串数组装入Mqladj Adj_arr结构数组中-------------
                     
               k=k-2;
               ArrayResize(Adj_arr,k);
               
               for(int i=0;i<k;i++)
               {
                 string su_Arr[];
                 sep=",";
                 u_sep=StringGetCharacter(sep,0);
                 int m=StringSplit(trade_Arr[i],u_sep,su_Arr); 
                 
                 //MessageBox(su_Arr[1]+","+su_Arr[2]+","+su_Arr[3]);
                 
                 Adj_arr[i].time=StringToTime(su_Arr[2]);
                 Adj_arr[i].adj_data=StringToDouble(su_Arr[3]);

               }
               //---------------不同复权方式处理过程-----复权类型(只针对股票):None未复权 qfq前复权 hfq后复权 , 默认None--------------
                 //--------------向前复权--------qfq前复权------------------------
                  if(Adj=="qfq")
                     {
                       for(int i=1;i<ArraySize(Rates);i++)   
                       {
                       
                       double ad_0,ad_1;
                       MqlDateTime R_dt,A_dt;
                       TimeToStruct(Rates[i].time,R_dt);
                       
                          for(int n=1;n<ArraySize(Adj_arr);n++)
                          {
                           TimeToStruct(Adj_arr[n].time,A_dt);
                           
                           if( A_dt.year==R_dt.year && A_dt.mon==R_dt.mon &&  A_dt.day==R_dt.day) 
                           {
                           ad_1= Adj_arr[n].adj_data;
                           ad_0= Adj_arr[0].adj_data;
                           
                           //MessageBox(Adj_arr[n-1].time+","+Adj_arr[n-1].adj_data+","+Adj_arr[n].time+","+Adj_arr[n].adj_data);
                           }
                           
                          }
                
                       Rates[i].open  =Rates[i].open  *ad_1/ad_0;
                       Rates[i].close =Rates[i].close *ad_1/ad_0;
                       Rates[i].high  =Rates[i].high  *ad_1/ad_0;
                       Rates[i].low   =Rates[i].low   *ad_1/ad_0;
                       
                       }
                     }
                   //----------向后复权----hfq后复权--------------------------------
                    if(Adj=="hfq")
                     {
                       for(int i=0;i<ArraySize(Rates);i++)   
                       {
                       
                       double ad_0,ad_1;
                       MqlDateTime R_dt,A_dt;
                       TimeToStruct(Rates[i].time,R_dt);
                       
                          for(int n=0;n<ArraySize(Adj_arr);n++)
                          {
                           TimeToStruct(Adj_arr[n].time,A_dt);
                           
                           if( A_dt.year==R_dt.year && A_dt.mon==R_dt.mon &&  A_dt.day==R_dt.day) 
                           {
                           ad_1= Adj_arr[n].adj_data;
                           //ad_0= Adj_arr[0].adj_data;
                           
                           //MessageBox(Adj_arr[n-1].time+","+Adj_arr[n-1].adj_data+","+Adj_arr[n].time+","+Adj_arr[n].adj_data);
                           }
                           
                          }
         
                       Rates[i].open  =Rates[i].open  *ad_1;
                       Rates[i].close =Rates[i].close *ad_1;
                       Rates[i].high  =Rates[i].high  *ad_1;
                       Rates[i].low   =Rates[i].low   *ad_1;
                       
                       }
                     }
                  
               }
               else
                   PrintFormat("Error in download '%s', code %d", END_POINT_URL, res);
           }
           
}
//---------------------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------------------

图示:注意2019年6月4日与6月5日的向前复权后数据连续K线

300015.SZ 爱尔眼科 日线 向前复权



附件:完整的读取A股日线 及除权 复权过程源码

附加的文件: