加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Market Profile (市场功能 MT4 MT5指标)[MT5公式]

(2013-03-03 11:16:26)
标签:

杂谈

适用于MetaTrader 4和MetaTrader 5平台。

Market Profile MT4 MT5 指标 — 是一种经典的市场概况执行指标,可显示价格的密集程度,描述日交易时段最重要的价格水平,值域以及控制值。该指标可附加到5分钟、15分钟和30分钟图表,可显示日交易时段的市场概况。虽然5分钟时间框架的精确性更高,但由于30分钟时间框架具有更高的可视性,故推荐使用30分钟的时间框架。该指标有3种颜色组合可用于描绘市场阻力。该指标基于纯粹的价格行为,不使用任何标准MetaTrader指标。

输入参数:

StartFromDate (默认 = '') — 如果StartFromToday设置为false,那么该指标将从此日期描绘市场概况。它往回进行描绘。例如,如果您设置为2010.07.20,DaysToCount设置为2,那么它会描绘2010.07.20和2010.07.19的市场概况。

StartFromToday (默认 = true) — 如果设置为true,那么该指标将从今日开始描绘,否侧将从StartFromDate设置的日期开始。

DaysToCount (默认 = 2) — 指描绘市场概况的天数。

ColorScheme (默认 = 0) — c市场概况阻力的颜色组合:

0 — 蓝色到红色.

1 — 红色到绿色.

2 — 绿色到蓝色.

MedianColor (默认 = White) — 控制值(中值)的颜色。

ValueAreaColor (默认 = White) — 值域边界的颜色。

http://www.cxh99.com/UploadFiles/Article2/2012/5/201205131612542433.pngProfile (市场功能 MT4 MT5指标)[MT5公式]" />

图表屏幕显示了2日外汇交易时段计算得出的市场概况。时间框架为30分钟图表,第二日仍在进行当中。最早的价格标记为蓝色,最后的价格为红色。中值和值域使用白线标记,并显示最重要的价格区域。如果突破行为的交易量不够大,交易商更倾向于返回那些区域。以高交易量突破这些区域意味着真实的突破。了解更多关于市场概况的内容,请查阅此简短的电子书:市场概况书籍。

标签:MT4 MT5指标源码

MT4 指标源码

//+------------------------------------------------------------------+
//|                                                MarketProfile.mq4 |
//|                                       Copyright � 2010, EarnForex.com |
//|                                        http://www.cxh99.com/ |
//+------------------------------------------------------------------+
#property copyright "www.cxh99.com"
#property link      "www.cxh99.com"

#property indicator_chart_window

extern datetime StartFromDate  = D'';
extern bool      StartFromToday = true;
extern int          DaysToCount    = 2; // Number of days for which to count the Market Profile
extern int          ColorScheme      = 0; // 0 - Blue to Red, 1 - Red to Green, 2 - Green to Blue
extern color      MedianColor      = White;
extern color      ValueAreaColor = White;

int DigitsM;                     // Amount of digits normalized for standard 4 and 2 digits after dot
datetime StartDate;             // Will hold either StartFromDate or Time[0]
double onetick;                 // One normalized pip
int SecondsInPeriod;         // Will hold calculated amount of seconds in the selected timeframe period
bool FirstRunDone = false; // If true - OnCalculate() was already executed once

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
   IndicatorShortName("MarketProfile");

    // Normalizing the digits to standard 4- and 2-digit quotes
    if (Digits == 5) DigitsM = 4;
    else if (Digits == 3) DigitsM = 2;
    else DigitsM = Digits;

    if (Period() == PERIOD_M30) SecondsInPeriod = 1800;
    if (Period() == PERIOD_M15) SecondsInPeriod = 900;
    if (Period() == PERIOD_M5) SecondsInPeriod = 300;

    onetick = NormalizeDouble(1 / (MathPow(10, DigitsM)), DigitsM);
}

//+------------------------------------------------------------------+
//| Custor indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
{
    // Delete all rectangles (it takes too much time to delete exactly those rectangles that were created by this indicator)
    ObjectsDeleteAll(0, OBJ_RECTANGLE);
}

//+------------------------------------------------------------------+
//| Custom Market Profile main iteration function                    |
//+------------------------------------------------------------------+
int start()
{
    if ((Period() != PERIOD_M30) && (Period() != PERIOD_M15) && (Period() != PERIOD_M5))
    {
        Print("TimeFrame should be set to M30, M15 or M5.");
        return(-1);
    }

    if (StartFromToday) StartDate = Time[0];
    else StartDate = StartFromDate;
    // If we calculate profiles for the past days, no need to rerun it
    if ((FirstRunDone) && (StartDate != Time[0])) return(0);

    // Get start and end bar numbers of the given date
    int dayend = FindDayEndByDate(StartDate);
    int daystart = FindDayStart(dayend);

    int DayToStart = 0;
    // If all days have already been counted, jump to the current one
    if (FirstRunDone) DayToStart = DaysToCount - 1;
    else
    {
        // Move back to the oldest day to count to start from it
        for (int i = 1; i < DaysToCount; i++)
        {
            dayend = daystart + 1;
            daystart = FindDayStart(dayend);
        }
    }

    // We begin from the oldest day coming to today or to StartFromDate
    for (i = DayToStart; i < DaysToCount; i++)
    {
        double DayMax = -1, DayMin = 99999999999;
        // Find the day's high and low to
        for (int bar = daystart; bar >= dayend; bar--)
        {
            if (High[bar] > DayMax) DayMax = High[bar];
            if (Low[bar] < DayMin) DayMin = Low[bar];
        }
        DayMax = NormalizeDouble(DayMax, DigitsM);
        DayMin = NormalizeDouble(DayMin, DigitsM);
        int TPOperPrice[];
        // Possible price levels if multiplied to integer
        int max = MathRound(DayMax / onetick + 2); // + 2 because further we will be possibly checking array at DayMax + 1
        ArrayResize(TPOperPrice, max);
        ArrayInitialize(TPOperPrice, 0);
        int MaxRange = 0; // Maximum distance from day start to the drawn dot
        double PriceOfMaxRange = 0; // Level of the maximum range, required to draw Median
        double DistanceToCenter = 99999999; // Closest distance to center for the Median
        int TotalTPO = 0; // Total amount of dots (TPO's)
        // Going through all possible quotes from daily High to daily Low
        for (double price = DayMax; price >= DayMin; price -= onetick)
        {
            int range = 0; // Distance from first bar to the current bar

            // Going through all bars of the day to see if the price was encoutered here
            for (bar = daystart; bar >= dayend; bar--)
            {
                // Price is encountered in the given bar
                if ((price >= Low[bar]) && (price <= High[bar]))
                {
                    // Update maximum distance from day's start to the found bar (needed for Median)
                    if ((MaxRange < range) || ((MaxRange == range) && (MathAbs(price - (DayMin + (DayMax - DayMin) / 2)) < DistanceToCenter)))
                    {
                        MaxRange = range;
                        PriceOfMaxRange = price;
                        DistanceToCenter = MathAbs(price - (DayMin + (DayMax - DayMin) / 2));
                    }
                    // Draws rectangle
                    PutDot(price, Time[daystart], range, bar - daystart);
                    // Remember the number of encountered bars for this bars
                    int index = MathRound(price / onetick);
                    TPOperPrice[index]++;
                    range++;
                    TotalTPO++;
                }
            }
        }
        double TotalTPOdouble = TotalTPO;
        // Calculate amount of TPO's in the Value Area
        int ValueControlTPO = MathRound(TotalTPOdouble * 0.7);
        // Start with the TPO's of the Median
        index = PriceOfMaxRange / onetick;
        int TPOcount = TPOperPrice[index];

        // Go through the price levels above and below median adding the biggest to TPO count until the 70% of TPOs are inside the Value Area
        int up_offset = 1;
        int down_offset = 1;
        while (TPOcount < ValueControlTPO)
        {
            double abovePrice = PriceOfMaxRange + up_offset * onetick;
            double belowPrice = PriceOfMaxRange - down_offset * onetick;
            // If belowPrice is out of the day's range then we should add only abovePrice's TPO's, and vice versa
            index = MathRound(abovePrice / onetick);
            int index2 = MathRound(belowPrice / onetick);
            if (((TPOperPrice[index] >= TPOperPrice[index2]) || (belowPrice < DayMin)) && (abovePrice <= DayMax))
            {
                TPOcount += TPOperPrice[index];
                up_offset++;
            }
            else
            {
                TPOcount += TPOperPrice[index2];
                down_offset++;
            }
        }
        string LastName = " " + TimeToStr(Time[daystart], TIME_DATE);
        // Delete old Median
        if (ObjectFind("Median" + LastName) >= 0) ObjectDelete("Median " + LastName);
        // Draw a new one
        index = MathMax(daystart - MaxRange - 5, 0);
        ObjectCreate("Median" + LastName, OBJ_RECTANGLE, 0, Time[daystart + 16], PriceOfMaxRange, Time[index], PriceOfMaxRange + onetick);
        ObjectSet("Median" + LastName, OBJPROP_COLOR, MedianColor);
        ObjectSet("Median" + LastName, OBJPROP_STYLE, STYLE_SOLID);
       ObjectSet("Median" + LastName, OBJPROP_BACK, false);
        // Delete old Value Area
        if (ObjectFind("Value Area" + LastName) >= 0) ObjectDelete("Value Area " + LastName);
        // Draw a new one
        ObjectCreate("Value Area" + LastName, OBJ_RECTANGLE, 0, Time[daystart], PriceOfMaxRange + up_offset * onetick, Time[daystart] + (MaxRange + 1) * SecondsInPeriod, PriceOfMaxRange - down_offset * onetick);
        ObjectSet("Value Area" + LastName, OBJPROP_COLOR, ValueAreaColor);
        ObjectSet("Value Area" + LastName, OBJPROP_STYLE, STYLE_SOLID);
       ObjectSet("Value Area" + LastName, OBJPROP_BACK, false);

        // Go to the newer day only if there is one or more left
        if (DaysToCount - i > 1)
        {
            daystart = dayend - 1;
            dayend = FindDayEndByDate(Time[daystart]);
        }
    }
    FirstRunDone = true;

    return(0);
}

//+------------------------------------------------------------------+
//| Finds the day's starting bar number for any given bar number.    |
//| n - bar number for which to find starting bar.                        |
//+------------------------------------------------------------------+
int FindDayStart(int n)
{
    int x = n;
    while ((TimeDayOfYear(Time[n]) == TimeDayOfYear(Time[x])) && (x < Bars))
        x++;

    return(x - 1);
}

//+------------------------------------------------------------------+
//| Finds the day's end bar by the day's date.                                |
//+------------------------------------------------------------------+
int FindDayEndByDate(datetime date)
{
    int x = 0;

    while ((TimeDayOfYear(date) < TimeDayOfYear(Time[x])) && (x < Bars))
        x++;

    return(x);
}

//+------------------------------------------------------------------+
//| Puts a dot (rectangle) at a given position and color.                |
//| price and time are coordinates.                                                |
//| range is for the second coordinate.                                        |
//| bar is to determine the color of the dot.                                |
//+------------------------------------------------------------------+
void PutDot(double price, datetime time, int range, int bar)
{
    string LastName = " " + (time + range * SecondsInPeriod) + " " + DoubleToStr(price, 4);
    if (ObjectFind("MP" + LastName) >= 0) return;

    ObjectCreate("MP" + LastName, OBJ_RECTANGLE, 0, time + range * SecondsInPeriod, price, time + (range + 1) * SecondsInPeriod, price + onetick);
    // Color switching depending on the distance of the bar from the day's beginning
    int colour, offset1, offset2;
    switch(ColorScheme)
    {
        case 0:
            colour = DarkBlue;
            offset1 = 0x020000;
            offset2 = 0x000002;
        break;
        case 1:
            colour = DarkRed;
            offset1 = 0x000002;
            offset2 = 0x000200;
        break;
        case 2:
            colour = DarkGreen;
            offset1 = 0x000200;
            offset2 = 0x020000;
        break;
    }
    if (Period() == PERIOD_M30) colour += bar * offset1;
    else if (Period() == PERIOD_M15) colour += bar * (offset1 / 2);
    else colour += (bar / 3) * (offset1 / 2);
    if (Period() == PERIOD_M30) colour -= bar * offset2;
    else if (Period() == PERIOD_M15) colour -= bar * (offset2 / 2);
    else colour -= (bar / 3) * (offset2 / 2);
    ObjectSet("MP" + LastName, OBJPROP_COLOR, colour);
    // Fills rectangle
    ObjectSet("MP" + LastName, OBJPROP_BACK, true);
}
//+------------------------------------------------------------------+

 

MT5指标源码

//+------------------------------------------------------------------+
//|                                                MarketProfile.mq5 |
//|                                       Copyright � 2010, EarnForex.com |
//|                                        http://www.cxh99.com/ |
//+------------------------------------------------------------------+
#property copyright "www.cxh99.com"
#property link      "http://www.cxh99.com"
#property version   "1.01"

#property description "Displays the Market Profile indicator for the daily trading sessions."
#property description "Should be attached to M5, M15 or M30 timeframes."
#property description "M30 is recommended."
#property description ""
#property description "Designed for standard currency pairs. May work incorrectly with very exotic pairs, CFDs or commodities."
#property description "Be careful: it will delete all rectangle objects on the chart upon deinitialization."

#property indicator_chart_window

input datetime StartFromDate  = D'';
input bool        StartFromToday = true;
input int         DaysToCount       = 2; // Number of days for which to count the Market Profile
input int         ColorScheme     = 0; // 0 - Blue to Red, 1 - Red to Green, 2 - Green to Blue
input color     MedianColor     = White;
input color     ValueAreaColor = White;

int DigitsM;                     // Amount of digits normalized for standard 4 and 2 digits after dot
datetime StartDate;             // Will hold either StartFromDate or Time[0]
double onetick;                 // One normalized pip
int SecondsInPeriod;         // Will hold calculated amount of seconds in the selected timeframe period
bool FirstRunDone = false; // If true - OnCalculate() was already executed once

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
{
   IndicatorSetString(INDICATOR_SHORTNAME, "MarketProfile");

    // Normalizing the digits to standard 4- and 2-digit quotes
    if (_Digits == 5) DigitsM = 4;
    else if (_Digits == 3) DigitsM = 2;
    else DigitsM = _Digits;

    if (_Period == PERIOD_M30) SecondsInPeriod = 1800;
    if (_Period == PERIOD_M15) SecondsInPeriod = 900;
    if (_Period == PERIOD_M5) SecondsInPeriod = 300;

    onetick = NormalizeDouble(1 / (MathPow(10, DigitsM)), DigitsM);
}

//+------------------------------------------------------------------+
//| Custor indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // Delete all rectangles (it takes too much time to delete exactly those rectangles that were created by this indicator)
    ObjectsDeleteAll(0, 0, OBJ_RECTANGLE);
}

//+------------------------------------------------------------------+
//| Custom Market Profile main 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[])
{
    if ((_Period != PERIOD_M30) && (_Period != PERIOD_M15) && (_Period != PERIOD_M5))
    {
        Print("TimeFrame should be set to M30, M15 or M5.");
        return(-1);
    }

   ArraySetAsSeries(High, true);
   ArraySetAsSeries(Low, true);
   ArraySetAsSeries(Time, true);
    if (StartFromToday) StartDate = Time[0];
    else StartDate = StartFromDate;
    // If we calculate profiles for the past days, no need to rerun it
    if ((FirstRunDone) && (StartDate != Time[0])) return(rates_total);

    // Get start and end bar numbers of the given date
    int dayend = FindDayEndByDate(Time, StartDate, rates_total);
    int daystart = FindDayStart(Time, dayend, rates_total);

    int DayToStart = 0;
    // If all days have already been counted, jump to the current one
    if (FirstRunDone) DayToStart = DaysToCount - 1;
    else
    {
        // Move back to the oldest day to count to start from it
        for (int i = 1; i < DaysToCount; i++)
        {
            dayend = daystart + 1;
            daystart = FindDayStart(Time, dayend, rates_total);
        }
    }

    // We begin from the oldest day coming to today or to StartFromDate
    for (int i = DayToStart; i < DaysToCount; i++)
    {
        double DayMax = -1, DayMin = 99999999999;
        // Find the day's high and low to
        for (int bar = daystart; bar >= dayend; bar--)
        {
            if (High[bar] > DayMax) DayMax = High[bar];
            if (Low[bar] < DayMin) DayMin = Low[bar];
        }
        DayMax = NormalizeDouble(DayMax, DigitsM);
        DayMin = NormalizeDouble(DayMin, DigitsM);
        int TPOperPrice[];
        // Possible price levels if multiplied to integer
        int max = (int)(round(DayMax / onetick) + 2); // + 2 because further we will be possibly checking array at DayMax + 1
        ArrayResize(TPOperPrice, max);
        ArrayInitialize(TPOperPrice, 0);
        int MaxRange = 0; // Maximum distance from day start to the drawn dot
        double PriceOfMaxRange = 0; // Level of the maximum range, required to draw Median
        double DistanceToCenter = 99999999; // Closest distance to center for the Median
        int TotalTPO = 0; // Total amount of dots (TPO's)
        // Going through all possible quotes from daily High to daily Low
        for (double price = DayMax; price >= DayMin; price -= onetick)
        {
            int range = 0; // Distance from first bar to the current bar

            // Going through all bars of the day to see if the price was encoutered here
            for (int bar = daystart; bar >= dayend; bar--)
            {
                // Price is encountered in the given bar
                if ((price >= Low[bar]) && (price <= High[bar]))
                {
                    // Update maximum distance from day's start to the found bar (needed for Median)
                    if ((MaxRange < range) || (MaxRange == range) && (MathAbs(price - (DayMin + (DayMax - DayMin) / 2)) < DistanceToCenter))
                    {
                        MaxRange = range;
                        PriceOfMaxRange = price;
                        DistanceToCenter = MathAbs(price - (DayMin + (DayMax - DayMin) / 2));
                    }
                    // Draws rectangle
                    PutDot(price, Time[daystart], range, bar - daystart);
                    // Remember the number of encountered bars for this bars
                    TPOperPrice[(int)(price / onetick)]++;
                    range++;
                    TotalTPO++;
                }
            }
        }
        // Calculate amount of TPO's in the Value Area
        int ValueControlTPO = (int)((double)TotalTPO * 0.7);
        // Start with the TPO's of the Median
        int TPOcount = TPOperPrice[(int)(PriceOfMaxRange / onetick)];

        // Go through the price levels above and below median adding the biggest to TPO count until the 70% of TPOs are inside the Value Area
        int up_offset = 1;
        int down_offset = 1;
        while (TPOcount < ValueControlTPO)
        {
            double abovePrice = PriceOfMaxRange + up_offset * onetick;
            double belowPrice = PriceOfMaxRange - down_offset * onetick;
            // If belowPrice is out of the day's range then we should add only abovePrice's TPO's, and vice versa
            if (((TPOperPrice[(int)(abovePrice / onetick)] >= TPOperPrice[(int)(belowPrice / onetick)]) || (belowPrice < DayMin)) && (abovePrice <= DayMax))
            {
                TPOcount += TPOperPrice[(int)(abovePrice / onetick)];
                up_offset++;
            }
            else
            {
                TPOcount += TPOperPrice[(int)(belowPrice / onetick)];
                down_offset++;
            }
        }
        string LastName = " " + TimeToString(Time[daystart], TIME_DATE);
        // Delete old Median
        if (ObjectFind(0, "Median" + LastName) >= 0) ObjectDelete(0, "Median " + LastName);
        // Draw a new one
        ObjectCreate(0, "Median" + LastName, OBJ_RECTANGLE, 0, Time[daystart + 16], PriceOfMaxRange, Time[(int)(MathMax(daystart - MaxRange - 5, 0))], PriceOfMaxRange + onetick);
        ObjectSetInteger(0, "Median" + LastName, OBJPROP_COLOR, MedianColor);
        ObjectSetInteger(0, "Median" + LastName, OBJPROP_STYLE, STYLE_SOLID);
        // Delete old Value Area
        if (ObjectFind(0, "Value Area" + LastName) >= 0) ObjectDelete(0, "Value Area " + LastName);
        // Draw a new one
        ObjectCreate(0, "Value Area" + LastName, OBJ_RECTANGLE, 0, Time[daystart], PriceOfMaxRange + up_offset * onetick, Time[daystart] + (MaxRange + 1) * SecondsInPeriod, PriceOfMaxRange - down_offset * onetick);
        ObjectSetInteger(0, "Value Area" + LastName, OBJPROP_COLOR, ValueAreaColor);
        ObjectSetInteger(0, "Value Area" + LastName, OBJPROP_FILL, false);

        // Go to the newer day only if there is one or more left
        if (DaysToCount - i > 1)
        {
            daystart = dayend - 1;
            dayend = FindDayEndByDate(Time, Time[daystart], rates_total);
        }
    }
    FirstRunDone = true;

    return(rates_total);
}

//+------------------------------------------------------------------+
//| Finds the day's starting bar number for any given bar number.    |
//| n - bar number for which to find starting bar.                        |
//+------------------------------------------------------------------+
int FindDayStart(const datetime &Time[], int n, int rates_total)
{
    MqlDateTime dt1, dt2;
    int x = n;
    TimeToStruct(Time[n], dt1);
    TimeToStruct(Time[x], dt2);
    while ((dt1.day_of_year == dt2.day_of_year) && (x < rates_total))
    { 
        x++;
        TimeToStruct(Time[x], dt2);
    }
    return(x - 1);
}

//+------------------------------------------------------------------+
//| Finds the day's end bar by the day's date.                                |
//+------------------------------------------------------------------+
int FindDayEndByDate(const datetime &Time[], datetime date, int rates_total)
{
    MqlDateTime dt1, dt2;
    int x = 0;
    TimeToStruct(date, dt1);
    TimeToStruct(Time[x], dt2);
    while ((dt1.day_of_year < dt2.day_of_year) && (x < rates_total))
    { 
        x++;
        TimeToStruct(Time[x], dt2);
    }
    return(x);
}

//+------------------------------------------------------------------+
//| Puts a dot (rectangle) at a given position and color.                |
//| price and time are coordinates.                                                |
//| range is for the second coordinate.                                        |
//| bar is to determine the color of the dot.                                |
//+------------------------------------------------------------------+
void PutDot(double price, datetime time, int range, int bar)
{
    string LastName = " " + IntegerToString(time + range * SecondsInPeriod) + " " + DoubleToString(price);
    if (ObjectFind(0, "MP" + LastName) >= 0) return;

    ObjectCreate(0, "MP" + LastName, OBJ_RECTANGLE, 0, time + range * SecondsInPeriod, price, time + (range + 1) * SecondsInPeriod, price + onetick);
    // Color switching depending on the distance of the bar from the day's beginning
    int colour, offset1, offset2;
    switch(ColorScheme)
    {
        case 0:
            colour = DarkBlue;
            offset1 = 0x020000;
            offset2 = 0x000002;
        break;
        case 1:
            colour = DarkRed;
            offset1 = 0x000002;
            offset2 = 0x000200;
        break;
        case 2:
            colour = DarkGreen;
            offset1 = 0x000200;
            offset2 = 0x020000;
        break;
    }
    if (_Period == PERIOD_M30) colour += bar * offset1;
    else if (_Period == PERIOD_M15) colour += bar * (offset1 / 2);
    else colour += (bar / 3) * (offset1 / 2);
    if (_Period == PERIOD_M30) colour -= bar * offset2;
    else if (_Period == PERIOD_M15) colour -= bar * (offset2 / 2);
    else colour -= (bar / 3) * (offset2 / 2);
    ObjectSetInteger(0, "MP" + LastName, OBJPROP_COLOR, colour);
    // Fills rectangle
    ObjectSetInteger(0, "MP" + LastName, OBJPROP_FILL, true);
}
//+------------------------------------------------------------------+

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有