新人对MQL4和MQL5的任何问题,对算法和代码的帮助和讨论 - 页 985

 

下午好。

请帮助。当编写一个自定义函数时,测试者给出错误3,并且没有打开订单。请说明错误。

 ticket=OrderSend(Symbol(),OP_SELLSTOP,Lots,Prices_install(),3,0,0,NULL,MAGICNUMBER,0,clrGreen);
               if(ticket>0)//проверка отрытия позиции
                 {
                  if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
                     Print("SELL ордер открыт:",OrderOpenPrice());
                 }
               else
                  Print("Ошибка открытия ордера SELL:",GetLastError());
              }
            return;
           }
        }
 //+---------------------------------------------------------------------------+
      //|                     Условия модификации ордеров                           |
      //+---------------------------------------------------------------------------+

      if(ticket>0)
        {
         if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
           {
            //--- длинная позиция открыта
            switch(OrderType())
              {
               case OP_BUYSTOP:
                  if(NormalizeDouble(OrderOpenPrice()-Prices_install(),Digits)>0 && NormalizeDouble(Prices_install()-Bid,Digits)>0)
                    {
                     if(OrderModify(ticket,Prices_install(),0,0,0,clrBlue))
                        Print("Цена Price_BUY ордера успешно модифицирована.");
                     else
                        Print("Ошибка модификации ордера BUYStop. Код ошибки=",GetLastError());
                    }
                  else
                     Print("Цена модификации выше цены ордера");
                  break;
               case OP_SELLSTOP:
                  if(NormalizeDouble(Prices_install()-OrderOpenPrice(),Digits)>0 && NormalizeDouble(Ask-Prices_install(),Digits)>0)
                    {
                     if(OrderModify(ticket,Prices_install(),0,0,0,clrGreen))
                        Print("Цена Price_SELL ордера успешно модифицирована.");
                     else
                        Print("Ошибка модификации ордера SELLStop. Код ошибки=",GetLastError());
                    }
                  else
                     Print("Цена модификации выше цены ордера");
                  break;
               case OP_BUY:
                  if(NormalizeDouble(StopLosse_install()-OrderStopLoss(),Digits)>0 && NormalizeDouble(Ask-StopLosse_install(),Digits)>0)
                    {
                     if(OrderModify(ticket,OrderOpenPrice(),StopLosse_install(),0,0,clrBlue))
                        Print("Цена Price_BUY ордера успешно модифицирована.");
                     else
                        Print("Ошибка модификации ордера BUY. Код ошибки=",GetLastError());
                    }
                  else
                     Print("Цена модификации выше цены ордера");
                  break;
               case OP_SELL:
                  if(NormalizeDouble(OrderStopLoss()-StopLosse_install(),Digits)>0 && NormalizeDouble(StopLosse_install()-Bid,Digits)>0)
                    {
                     if(OrderModify(ticket,OrderOpenPrice(),StopLosse_install(),0,0,clrGreen))
                        Print("Цена Price_SELL ордера успешно модифицирована.");
                     else
                        Print("Ошибка модификации ордера SELL. Код ошибки=",GetLastError());
                    }
                  else
                     Print("Цена модификации выше цены ордера");
                  break;
              }
           }
        }
      Sleep(5);
     }
   return;
  }
//+------------------------------------------------------------------+
double StopLosse_install()
  {
   double StopLoss=iSAR(NULL,PERIODs_short_term,Step,Maximum,1);
   double StopLoss_install;
   int StopLevel=(int)MarketInfo(Symbol(),MODE_STOPLEVEL); // Минимально допустимый уровень стоп-лосса/тейк-профита в пунктах
   int FreezeLevel=(int)MarketInfo(Symbol(),MODE_FREEZELEVEL);//Уровень заморозки ордеров в пунктах
   switch(OrderType())
     {
      case 0:
         if(Bid-StopLoss>FreezeLevel)
           {
            if(Bid-StopLoss>=StopLevel)
               StopLoss_install=NormalizeDouble(StopLoss,Digits);
            if(Bid-StopLoss<StopLevel)
               StopLoss_install=NormalizeDouble(StopLoss-StopLevel*MarketInfo(Symbol(),MODE_DIGITS),Digits);
           }
         else
            Print("Цена открытия ордера находится в дистанции заморозки:",GetLastError());
         break;
      case 1:
         if(StopLoss-Ask>FreezeLevel)
           {
            if(StopLoss-Ask>=StopLevel)
               StopLoss_install=NormalizeDouble(StopLoss,Digits);
            if(StopLoss-Ask<StopLevel)
               StopLoss_install=NormalizeDouble(StopLoss+StopLevel*MarketInfo(Symbol(),MODE_DIGITS),Digits);
           }
         else
            Print("Цена открытия ордера находится в дистанции заморозки:",GetLastError());
         break;
     }
     return(StopLoss_install);
  }
//+------------------------------------------------------------------+
double Prices_install()
  {
   double Price=iSAR(NULL,PERIODs_short_term,Step,Maximum,1);
   double Price_install;
   int StopLevel=(int)MarketInfo(Symbol(),MODE_STOPLEVEL); // Минимально допустимый уровень стоп-лосса/тейк-профита в пунктах
   int FreezeLevel=(int)MarketInfo(Symbol(),MODE_FREEZELEVEL);//Уровень заморозки ордеров в пунктах
   switch(OrderType())
     {
      case 4:
         if(Price-Ask>FreezeLevel)
           {
            if(Price - Ask>=StopLevel)
               Price_install=NormalizeDouble(Price,Digits);
            if(Price - Ask<StopLevel)
               Price_install=NormalizeDouble(Price+StopLevel*MarketInfo(Symbol(),MODE_DIGITS),Digits);
           }
         else
            Print("Цена открытия ордера находится в дистанции заморозки:",GetLastError());
         break;
      case 5:
         if(Bid-Price>FreezeLevel)
           {
            if(Bid-Price>=StopLevel)
               Price_install=NormalizeDouble(Price,Digits);
            if(Bid-Price<StopLevel)
               Price_install=NormalizeDouble(Price+StopLevel*MarketInfo(Symbol(),MODE_DIGITS),Digits);
           }
         else
            Print("Цена открытия ордера находится в дистанции заморозки:",GetLastError());
         break;
     }
     return(Price_install);
  }
//+------------------------------------------------------------------+
 
Ivan Butko:

奇怪的是,如果在一个EA中,它为每个指定的货币对一个接一个地打开交易...有一个时间差。当你把它放在5个不同货币对的图表上时(符号(0)),当你按下自动交易时,所有5笔交易都会立即同时开启。

你能建议一下可能是什么原因,以及如何解决这个问题吗?把所有的东西都放在一个EA和一个图表上(这样,图表上的按钮 打开所有的交易,就像在原始版本中用几个EA激活 "自动交易 "时一样快)

一般来说,这里的问题是....你和我正在尽最大努力暂停终端,imho....我不喜欢写错误的逻辑,在这里我已经按照你的要求做了--把EA扔在图表上,按下按钮,它将试图在一个无尽的循环中打开一个订单。

#property copyright "IgorM"
#property link      "https://www.mql5.com/ru/users/igorm"
#property version   "1.00"
#property strict
input string   sym1 = "EURUSD";
input double   lot1 = 0.01;
input string   sym2 = "GBPUSD";
input double   lot2 = 0.01;
input string   sym3 = "USDCAD";
input double   lot3 = 0.01;
input string   sym4 = "USDJPY";
input double   lot4 = 0.01;
input string   sym5 = "AUDUSD";
input double   lot5 = 0.01;
#include <Controls\Button.mqh>
CButton ButtonSend;

string sym[5];
double lot[5];
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

int OnInit()
  {
   sym[0] = sym1;
   sym[1] = sym2;
   sym[2] = sym3;
   sym[3] = sym4;
   sym[4] = sym5;
   lot[0] = lot1;
   lot[1] = lot2;
   lot[2] = lot3;
   lot[3] = lot4;
   lot[4] = lot5;
   ButtonSend.Create(0, "ButtonSend" + _Symbol, 0, 10, 50, 100, 90);
   ButtonSend.Color(clrRed);
   ButtonSend.Text("Kill Forex!");
   OnTick();
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   static int ticket[5] = {-1, -1, -1, -1, -1 };
   if(ticket[0] > 0 && ticket[1] > 0 && ticket[2] > 0 && ticket[3] > 0 && ticket[4] > 0)
   {
    ButtonSend.Text("EA Stop");
    return;
    }
   while(!IsStopped())
   {
   if(ButtonSend.Pressed())
     {
      ButtonSend.Text("Sending...");
         if(TerminalInfoInteger(TERMINAL_CONNECTED) && TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && !IsTradeContextBusy())
           {
            RefreshRates();
            for(int i = 0; i < 5; i++)
              {
               if(sym[i] == "") ticket[i] = INT_MAX;
               if(ticket[i] > 0) continue;
               double vol = NormalizeLot(lot[i],sym[i]);
               ticket[i] = OrderSend(sym[i], OP_SELL, vol, SymbolInfoDouble(sym[i],SYMBOL_BID), 3, 0, 0, "", 0, 0, clrRed);
              }
           }
        }
     if(ticket[0] > 0 && ticket[1] > 0 && ticket[2] > 0 && ticket[3] > 0 && ticket[4] > 0) break;        
     ChartRedraw();      
     Sleep(123);
     }
  }
//+------------------------------------------------------------------+
double NormalizeLot(const double value,const string sym_,bool up=false)
  {
   double res,sizetable[9]={1.0,0.1,0.01,0.001,0.0001,0.00001,0.000001,0.0000001,0.00000001};
   double lotStep=SymbolInfoDouble(sym_,  SYMBOL_VOLUME_STEP);
   int lotdigits;
   for(lotdigits=8; lotdigits>=0; lotdigits--) if(lotStep<=sizetable[lotdigits]) break;
   if(up) res=NormalizeDouble(MathCeil(MathMin(MathMax(value,SymbolInfoDouble(sym_,SYMBOL_VOLUME_MIN)),SymbolInfoDouble(sym_,SYMBOL_VOLUME_MAX))/lotStep)*lotStep,lotdigits);
   else res=NormalizeDouble(MathFloor(MathMin(MathMax(value,SymbolInfoDouble(sym_,SYMBOL_VOLUME_MIN)),SymbolInfoDouble(sym_,SYMBOL_VOLUME_MAX))/lotStep)*lotStep,lotdigits);
   return(res);
  }
//+------------------------------------------------------------------+
 
Igor Makanu:

一般来说,这里的问题是....你和我都在尽最大努力中止终端,imho....我不喜欢写错误的逻辑,在这里我按照你的要求做了--把EA扔到图表上,按下按钮,它将试图在一个无尽的循环中打开一个订单。

伊戈尔,感谢你提供的扩展功能!不幸的是,在速度方面没有任何变化。我的交易也在一个接一个地打开。我又试着在每个图表上放最小的版本,并按下 "自动交易",所有的交易一下子就打开了,而且是即时的。
尽管这样,仍然当你点击综合交易面板按钮时,单笔交易打开的速度更快,没有最初的延迟。很奇怪。我们希望达到一种状态,即类似的按钮可以毫无延迟地打开一个篮子。

执行速度 而言:
1.最快的方法是捅破标准贸易面板。
2.将其散布在几个图表上,然后按下 "自动交易 "按钮。
3.逐一打开篮子的按钮

 
Ivan Butko:

交易也是一个接一个地打开。

没有其他办法,谷歌关于市场执行/即时执行账户类型的信息

和第二点https://www.mql5.com/ru/docs/runtime/running

专家顾问 - 在它自己的线程中,有多少个专家顾问就有多少个执行线程

简而言之--在EA发送订单后,它等待订单被确认,如果有几个EA在不同的图表上,你会得到每个EA的独立性,即多线程执行代码。


我认为对于你的问题,就我对新闻交易的理解而言,最好的解决办法是打开几个图表,把配置在上面的EA扔出去,这些EA是无限循环的,在下单后,EA必须离开图表,用自动交易按钮启动所有EA。

 
大家好!我如何让EA中的标准抛物线指标在创建第一个点(即市场趋势的变化)时打开交易器????。
 
ponochka:
大家好!如何使EA中的标准抛物线指标在其第一个点(即市场趋势变化)创建时打开交易器????。

起初,箭头所指向的点是在当前价格 的对面。而价格一触及该点,就立即跳到了另一边。

结论:当价格触及底部的点时,将是顶部的第一个点。反之亦然...

 
Igor Makanu:

没有其他办法,谷歌关于市场执行/即时执行账户类型的信息

而第二点是https://www.mql5.com/ru/docs/runtime/running

简而言之,当你发送订单时,EA等待订单被确认,如果你在不同的图表上有几个EA,那么你会得到每个EA的独立性,即代码的多线程执行


我认为对于你的问题,就我对交易新闻的理解,最好的解决办法是打开几个图表,把配置在上面的EA扔出去,这些EA是无限循环的,在下单后,EA应该离开图表,用自动交易按钮启动所有EA。

哇...谢谢你的澄清。我的意思是。事实上,只有在打开当前的一个后,下一个才会打开。好奇。

你是否知道,如果你尝试第四个方案--交易复制器--交易也会按顺序打开?例如,我们给复制者一个信号,在向导上似乎有7个订单是开放的...。他将如何打开它们?或者,我们也应该设立7个复制者,并向他们每个人指示复制他们的对子。虽然,我可能会尝试,但我不需要额外创造什么。

还有第五种方法--使用点击器。但是,它太贵了,我认为目前没有人愿意写它。我将尝试目前可用的东西。


UPD

我试过复印机--同样的事情,交易按顺序打开。而如果我把复印机放在7个图表上,并设置1ms的更新,终端和UPD一起会爆炸的。

那就剩下两条路了。

1.主动的--将专家顾问放在每个图表上并启用自动交易。
2)未经证实的--要使用第三方软件--点击器。
 
Ivan Butko:

1.可操作性 - 在每个图表上放一个EA,并启用自动交易。

2.未经证实的--使用第三方软件--点击器。

99%的概率1和2将以同样的速度工作,服务器仍在逐一处理你的交易请求,当你从7个图表中发送7个请求时,你赢得了等待服务器答复的时间(直到EA收到票据,它什么也不做 - 它在等待)。

关于点击器,用WinAPI你可以用鼠标 "点击 "屏幕上的任何一点,我一个月前检查了代码https://www.mql5.com/ru/forum/156025#comment_7552799

你想用自动点击器来发送订单,你也可以用@Koldun Zloy的 代码来做。

 
Igor Makanu:

99%的概率,选项1和2将以同样的速度工作,服务器仍在逐一处理你的交易请求,当你从7个图表中发送7个请求时,你赢得了等待服务器回复的时间(直到EA收到票据,它什么都不做 - 它在等待)。

关于点击器,用WinAPI你可以用鼠标在屏幕的任何一点上 "点击",我一个月前检查了代码https://www.mql5.com/ru/forum/156025#comment_7552799。

你想用自动点击器来发送订单,你也可以用@Koldun Zloy的 代码来做。

太好了,我去看看。非常感谢您的帮助和您所做的工作。

 

你好。

我正在掌握MT5。但如何画出缓冲线并不清楚。我从mql5网站下载了指标,并对其进行了一些修正。

但现在我在线条方面遇到了一些问题。我的问题是:为什么历史上的线是倾斜的,如何解决这个问题?

//+------------------------------------------------------------------+
//|                                                        Proba.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
//---- для расчета и отрисовки индикатора использовано пятнадцать буферов
#property indicator_buffers 4
//---- использовано пятнадцать графических построений
#property indicator_plots   4
//+----------------------------------------------+
//|  Параметры отрисовки уровня                  |
//+----------------------------------------------+
//---- отрисовка индикатора в виде значка
#property indicator_type1   DRAW_LINE
//---- в качестве цвета линии индикатора цвет MediumSeaGreen
#property indicator_color1 clrGold
//---- толщина линии индикатора равна
#property indicator_width1  1
//---- отображение метки индикатора
#property indicator_label1  "OPEN"
//+----------------------------------------------+
//|  Параметры отрисовки уровня                  |
//+----------------------------------------------+
//---- отрисовка индикатора в виде значка
#property indicator_type2   DRAW_LINE
//---- в качестве цвета индикатора использован цвет MediumSeaGreen
#property indicator_color2 clrRed
//---- толщина линии индикатора равна
#property indicator_width2  1
//---- отображение лэйбы индикатора
#property indicator_label2  "CLOSE"
//+----------------------------------------------+
//|  Параметры отрисовки уровня                  |
//+----------------------------------------------+
//---- отрисовка индикатора в виде значка
#property indicator_type3   DRAW_LINE
//---- в качестве цвета индикатора использован цвет Lime
#property indicator_color3 clrYellow
//---- толщина линии индикатора равна
#property indicator_width3  2
//---- отображение метки индикатора
#property indicator_label3  "HIGH"
//+----------------------------------------------+
//|  Параметры отрисовки уровня                  |
//+----------------------------------------------+
//---- отрисовка индикатора в виде значка
#property indicator_type4   DRAW_LINE
//---- в качестве цвета индикатора использован цвет MediumSeaGreen
#property indicator_color4 clrYellow
//---- толщина индикатора равна
#property indicator_width4  1
//---- отображение метки индикатора
#property indicator_label4  "LOW"
//+----------------------------------------------+
//| Входные параметры индикатора                 |
//+----------------------------------------------+
input int  ExtHowManyDays=10; //Количество Дней истории
input ENUM_TIMEFRAMES Timeframes = PERIOD_D1; //Период расчетов
//+----------------------------------------------+
//---- объявление динамических массивов, которые в дальнейшем
//---- будут использованы в качестве индикаторных буферов
double BufferLow[],BufferHigh[],BufferClose[],BufferOpen[];
//+------------------------------------------------------------------+
//| iBarShift() function                                             |
//+------------------------------------------------------------------+
int iBarShift(string symbol,ENUM_TIMEFRAMES timeframe,datetime time)
  {
//----
   if(time<0)
      return(-1);
   datetime Arr[],time1;

   time1=(datetime)SeriesInfoInteger(symbol,timeframe,SERIES_LASTBAR_DATE);

   if(CopyTime(symbol,timeframe,time,time1,Arr)>0)
     {
      int size=ArraySize(Arr);
      return(size-1);
     }
   else
      return(-1);
//----
  }
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
  {
//---
   int draw_begin;
   if(ExtHowManyDays < 1)
      draw_begin=0;
   else
      draw_begin=ExtHowManyDays;

//---- превращение динамических массивов в индикаторные буферы
   SetIndexBuffer(0,BufferOpen,INDICATOR_DATA);
   SetIndexBuffer(1,BufferClose,INDICATOR_DATA);
   SetIndexBuffer(2,BufferHigh,INDICATOR_DATA);
   SetIndexBuffer(3,BufferLow,INDICATOR_DATA);
//---- создание метки для отображения в DataWindow
   PlotIndexSetString(0,PLOT_LABEL,"Price OPEEN");
   PlotIndexSetString(1,PLOT_LABEL,"Price CLOSE");
   PlotIndexSetString(2,PLOT_LABEL,"Price HIGH");
   PlotIndexSetString(3,PLOT_LABEL,"Price LOW");
//---- индексация элементов в буферах как в таймсериях
   ArraySetAsSeries(BufferOpen,true);
   ArraySetAsSeries(BufferClose,true);
   ArraySetAsSeries(BufferHigh,true);
   ArraySetAsSeries(BufferLow,true);
//---- определение точности отображения значений индикатора
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//--- установим номер бара, с которого будет идти отрисовка
   for(int q=0; q<=4; q++)
      PlotIndexSetInteger(q,PLOT_DRAW_BEGIN,draw_begin); //ПРОБЛЕМА ТУТ!!!!!
      //PlotIndexSetInteger(q,PLOT_SHIFT,0);
      //PlotIndexSetDouble(q,PLOT_EMPTY_VALUE,EMPTY_VALUE);
//----
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//---- объявления локальных переменных
   int    begin_bar,first_bar,last_bar,cnt,copy;
   double High_=0.0,Low_=0.0,Close_=0.0,Open_=0.0;
   double iClose[],iOpen[],iHigh[],iLow[];
   datetime iTime[];
//---
   if(_Period>=Timeframes)
      return(0);
//---- проверка и установка начального бара
   if(ExtHowManyDays < 1)
      begin_bar=Bars(NULL,Timeframes)-2;
   else
      begin_bar=ExtHowManyDays-1;

//---- индексация элементов в массивах как в таймсериях
   ArraySetAsSeries(time,true);
   ArraySetAsSeries(iTime,true);
   ArraySetAsSeries(iOpen,true);
   ArraySetAsSeries(iClose,true);
   ArraySetAsSeries(iHigh,true);
   ArraySetAsSeries(iLow,true);

   copy=begin_bar+2;

   if(CopyTime(NULL,Timeframes,0,copy,iTime)<copy)
      return(0);
   if(CopyOpen(NULL,Timeframes,0,copy,iOpen)<copy)
      return(0);
   if(CopyClose(NULL,Timeframes,0,copy,iClose)<copy)
      return(0);
   if(CopyHigh(NULL,Timeframes,0,copy,iHigh)<copy)
      return(0);
   if(CopyLow(NULL,Timeframes,0,copy,iLow)<copy)
      return(0);

//----
   /*cnt=0;
   while(true)
     {
      if(iTime[0]>=(time[0]-Timeframes*60))
         break;
      cnt++;
      if(cnt>5)
         return(0);
      Sleep(300); //1000
     }*/
//----
   if(prev_calculated!=0)
     {
      begin_bar=0;
      BufferOpen[cnt]=0.0;
      BufferClose[cnt]=0.0;
      BufferHigh[cnt]=0.0;
      BufferLow[cnt]=0.0;
     }
//----
   for(cnt=begin_bar; cnt>=0; cnt--)
     {
      if(cnt<rates_total)
        {
         Open_=iOpen[cnt];
         Close_=iClose[cnt+1];
         High_=iHigh[cnt+1];
         Low_=iLow[cnt+1];
        }
      first_bar=iBarShift(NULL,_Period,iTime[cnt]);

      if(cnt>0)
         last_bar=iBarShift(NULL,_Period,iTime[cnt-1]);
      else
         last_bar=0;

      while(first_bar>=last_bar)
        {
         if((first_bar==last_bar && last_bar>0) || first_bar<0)
            break;

         BufferOpen[first_bar]=Open_;
         BufferClose[first_bar]=Close_;
         BufferHigh[first_bar]=High_;
         BufferLow[first_bar]=Low_;
         if(BufferOpen[first_bar]!=BufferOpen[first_bar+1])
            BufferOpen[first_bar+1]=EMPTY_VALUE;
         if(BufferClose[first_bar]!=BufferClose[first_bar+1])
            BufferClose[first_bar+1]=EMPTY_VALUE;
         if(BufferHigh[first_bar]!=BufferHigh[first_bar+1])
            BufferHigh[first_bar+1]=EMPTY_VALUE;
         if(BufferLow[first_bar]!=BufferLow[first_bar+1])
            BufferLow[first_bar+1]=EMPTY_VALUE;

         first_bar--;
        }
     }
//----
   ChartRedraw(0);
//----
   return(rates_total);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+



原因: