寫了一個工具分析基金回撤,分享實現過程

  最近有一個網友讓我幫他寫一個工具分析基金回撤狀況,前幾天項目比較忙就直沒動手,今天晚上有點時間,研究了一下。html

先把今天的研究成果分享:node

  要分析基金淨值回撤,首先確定要有基金的淨值變化數據。要抓數據確定是到每天基金網上抓,畢竟人家是專業機構。ios

我找了一隻我的比較喜歡的基金,易方達中小盤混合 (110011),在每天基金網站上很容易找到它的歷史淨值頁面:web

  http://fundf10.eastmoney.com/jjjz_110011.htmlwindows

  由於以前抓過每天基金當日基金淨值數據,知道它的頁面數據是藏在js裏面,在瀏覽器訪問頁面後,執行js從服務器動態獲取再展現到瀏覽器。api

  要知道是哪一個js腳本,方法有不少。我用的是谷歌瀏覽器自帶的開發者工具,以下圖:瀏覽器

  打開開發者工具的狀況下瀏覽基金淨值頁面,就看到該頁面訪問的全部資源網址。數據比較亂,不過關心的只是js,因此按類型排了序。不過能夠看出來訪問的js也很多,暫時還很差知道是哪個js包含我要的數據。服務器

  因爲默認只顯示了第一頁數據,因此我就隨意點了其它頁淨值,從訪問的資源列表變化狀況,很容易發現想要的js網址,如圖網絡

  把這些帶有callback的js網址拷下來,以下:curl

http://api.fund.eastmoney.com/f10/lsjz?callback=jQuery183018519977574130597_1558194911277&fundCode=110011&pageIndex=6&pageSize=20&startDate=&endDate=&_=1558195435735

  從命令參數上猜想fundCode是基金代碼,pageIndex是淨值頁面頁碼,其它參數暫時不知道意義。

  先嚐試使用這個網址在瀏覽器訪問看是啥狀況:

  

  能夠看出網站對這個js訪問作了控制,不讓用戶直接在瀏覽器訪問。 因爲對於http抓包不大熟悉,不清楚這種狀況下要怎麼處理,總不至於須要寫一個瀏覽器來抓一個js數據吧。

因而找了一個熟悉抓包的朋友諮詢一下。諮詢結果明天再說,跟下面這個請求有關。

GET /f10/lsjz?callback=jQuery183018519977574130597_1558194911277&fundCode=110011&pageIndex=4&pageSize=20&startDate=&endDate=&_=1558195568400 HTTP/1.1
Host: api.fund.eastmoney.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
Accept: */*
Referer: http://fundf10.eastmoney.com/jjjz_110011.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

   -----------------------------------------------

  接上文,這是訪問基金淨值頁面的http請求報文頭。注意裏面的Referer。百度百科上是這樣說的:HTTP Referer是header的一部分,當瀏覽器向web服務器發送請求的時候,通常會帶上Referer,告訴服務器我是從哪一個頁面連接過來的,服務器基此能夠得到一些信息用於處理。

  這裏每天基金網站就是用Referer來防止別人盜鏈的。我直接在瀏覽器輸入js網址,發送給服務器的請求裏面的Referer是空的,但若是是在網頁上點擊,則Referer是網頁的網址。

  爲了驗證加上referer加上後是否能正常訪問基金淨值數據,我用curl作了一個試驗:

curl "http://api.fund.eastmoney.com/f10/lsjz?callback=jQuery183018519977574130597_1558194911277&fundCode=110011&pageIndex=1&pageSize=300&startDate=2018-01-01&endDate=2018-12-31&_=1558194929451" --referer "http://fundf10.eastmoney.com/" --user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36" -o tmp.txt

  試驗成功,正常獲取到了淨值。

  -----------------------------------------------

  獲取基金的方法找到了,接下來再看一下js網址:http://api.fund.eastmoney.com/f10/lsjz?callback=jQuery183018519977574130597_1558194911277&fundCode=110011&pageIndex=6&pageSize=20&startDate=&endDate=&_=1558195435735

  簡單猜想,這裏面的fundCode是基金代碼,pageIndex是頁面編號,pageSize是每頁淨值數量,startDate和endDate是淨值日期區間,若是要獲取一年的淨值,可使用

  pageIndex = 1,pageSize = 300, startDate = 2019-01-01,endDate = 2019-12-31,用curl再試驗一下,猜想得對。

  接下來就是體力活了,再寫一個程序循環獲取每一年淨值,而後再進行分析就能夠。

  -----------------------------------------------

看我分析110011 易方達中小盤的結果

 

   能夠看出易方達中小盤2008年成立,至今總收益是370%,也就是10年前把一萬投入該基金而且設置了分成再投資,如今理論上市值有3萬7。

在這期間買入該基金,最慘的是在2015-08-19,從那天起基金連續下跌,直到2015-08-25纔開始止跌,期間總共跌幅接近15%。

最幸運的是在2015-07-08買入基金的投資者,他們買了後基金就一直上漲,直到漲了20%纔開始回調。

  今年易方達中小的業績以下:

分析起止日期:[2019-01-02,2019-06-06]
區間回撤/上漲率: 29.92% [2019-01-02, 2019-06-06] [3.6184, 4.7011]
---------------------
最大連續回撤率: -5.21% [2019-04-30, 2019-05-06] [5.0403, 4.7775]
期間最大回撤率: -7.92% [2019-04-10, 2019-05-09] [5.1024, 4.6981]
從起始時間算最大回撤率: -1.34% [2019-01-02, 2019-01-03] [3.6184, 3.5699]
最大連續回撤天數率: 4 [2019-03-04, 2019-03-08] [4.5156, 4.2940]
---------------------
最大連續上漲率: 9.22% [2019-01-30, 2019-02-14] [3.8597, 4.2157]
期間最大上漲率: 42.93% [2019-01-03, 2019-04-10] [3.5699, 5.1024]
從起始時間算最大上漲率: 41.01% [2019-01-02, 2019-04-10] [3.6184, 5.1024]
最大連續上漲天數率: 6 [2019-01-30, 2019-02-14] [3.8597, 4.2157]

   

  今年基金還不錯的,接近30%,最賺錢的時候是20190410,那時基金賺了41%

-------------------------------------------------------------

  有興趣的同窗能夠到這裏下載我這個小工具:

連接: https://pan.baidu.com/s/1x7S8X5Y5tdtPPdUNI92nJA 提取碼: zg97 複製這段內容後打開百度網盤手機App,操做更方便哦

 

-------------------------------------------------------------

相關代碼(寫得比較簡陋,由於想到一點寫一點,之後有時間再考慮優化)

getRetracement.h

#pragma once

#include "stdafx.h"
#include <stdio.h>
#include <iomanip>
typedef struct __tagNetValueInfo{
    string strNetValueDate;
    double dAccumulatedNet;

    __tagNetValueInfo()
    {
        dAccumulatedNet = 0;
    }

    __tagNetValueInfo(const string& v1, double v2)
    {
        strNetValueDate = v1;
        dAccumulatedNet = v2;
    }
}NetValueInfo;

typedef struct __StatisticalNetValueInfo{
    string strValue;
    string strNode;
    string strBegDate;
    double dBegNetValue;
    string strEndDate;
    double dEndNetValue;

    __StatisticalNetValueInfo()
    {
        dBegNetValue = dEndNetValue = 0;
        strBegDate = strEndDate = "-";
    }

    string getString()
    {
        string strRet;
        char buf[1024];
        size_t nStrNodeLen = strNode.size() + 1;
        string strPad = "";
        for (size_t i = nStrNodeLen / 8; i < 3; ++i)
        {
            strPad += "\t";
        }
        _snprintf(buf, sizeof(buf), "%s:%s%s\t",
            strNode.c_str(), strPad.c_str(), strValue.c_str());
        strRet += buf;

        _snprintf(buf, sizeof(buf), "[%s,%s]\t",
            strBegDate.c_str(), strEndDate.c_str());
        strRet += buf;

        _snprintf(buf, sizeof(buf), "[%04f,%04f]\n",
            dBegNetValue, dEndNetValue);
        strRet += buf;

        return strRet;
    }

    void print()
    {
        size_t nStrNodeLen = strNode.size() + 1;
        string strPad = "";
        for (size_t i = nStrNodeLen / 8; i < 3; ++i)
        {
            strPad += "\t";
        }
        cout << strNode << ":" << strPad << strValue << "\t";// << endl;
        cout << "[" << strBegDate << ", " << strEndDate << "]\t";// << endl;
        cout.fill('0');
        cout << "[" << fixed << setprecision(4) << dBegNetValue 
            << ", "  << fixed << setprecision(4) << dEndNetValue 
            << "]" << endl;
    }
}StatisticalNetValueInfo;

typedef struct __tagResultInfo{
    //最大連續回撤率/上漲率信息
    StatisticalNetValueInfo resMaxContinRetraceInfo;
    StatisticalNetValueInfo resMaxContinRiseInfo;

    //最大連續回撤天數/上漲天數信息
    StatisticalNetValueInfo resMaxContinRetraceDaysInfo;
    StatisticalNetValueInfo resMaxContinRiseDaysInfo;

    //最大回撤率/上漲率信息
    StatisticalNetValueInfo resMaxRetraceInfo;
    StatisticalNetValueInfo resMaxRiseInfo;

    //從起始時間算最大回撤率/上漲率信息
    StatisticalNetValueInfo resMaxRetraceFromBeginInfo;
    StatisticalNetValueInfo resMaxRiseFromBeginInfo;


    //從起始時間算回撤率/上漲率信息
    StatisticalNetValueInfo resRetraceRiseFromBeginInfo;

    __tagResultInfo()
    {
        resMaxContinRetraceInfo.strNode = "最大連續回撤率";
        resMaxContinRetraceDaysInfo.strNode = "最大連續回撤天數率";
        resMaxRetraceInfo.strNode = "期間最大回撤率";
        resMaxRetraceFromBeginInfo.strNode = "從起始時間算最大回撤率";
        resMaxContinRiseInfo.strNode = "最大連續上漲率";
        resMaxContinRiseDaysInfo.strNode = "最大連續上漲天數率";
        resMaxRiseInfo.strNode = "期間最大上漲率";
        resMaxRiseFromBeginInfo.strNode = "從起始時間算最大上漲率";

        resRetraceRiseFromBeginInfo.strNode = "區間回撤/上漲率";
    }

    string getString()
    {
        string strRet;
        strRet += resRetraceRiseFromBeginInfo.getString();
        strRet += "---------------------\n";
        strRet += resMaxContinRetraceInfo.getString();
        strRet += resMaxRetraceInfo.getString();
        strRet += resMaxRetraceFromBeginInfo.getString();
        strRet += resMaxContinRetraceDaysInfo.getString();
        strRet += "---------------------\n";
        strRet += resMaxContinRiseInfo.getString();
        strRet += resMaxRiseInfo.getString();
        strRet += resMaxRiseFromBeginInfo.getString();
        strRet += resMaxContinRiseDaysInfo.getString();

        return strRet;
    }

    void print()
    {
        resRetraceRiseFromBeginInfo.print();
        cout << "---------------------" << endl;
        resMaxContinRetraceInfo.print();
        resMaxRetraceInfo.print();
        resMaxRetraceFromBeginInfo.print();
        resMaxContinRetraceDaysInfo.print();
        cout << "---------------------" << endl;
        resMaxContinRiseInfo.print();
        resMaxRiseInfo.print();
        resMaxRiseFromBeginInfo.print();
        resMaxContinRiseDaysInfo.print();
    }
}ResultInfo;

int getRectracement(const vector<NetValueInfo> &vecNetValueInfo, size_t nBegPos, size_t nEndPos,
    OUT ResultInfo& resultInfo);

void testGetRectracement();

 

stdafx.h

// stdafx.h : 標準系統包含文件的包含文件,
// 或是常用但不常更改的
// 特定於項目的包含文件
//

#pragma once


#define WIN32_LEAN_AND_MEAN        // 從 Windows 頭中排除極少使用的資料
#include <stdio.h>
#include <tchar.h>



// TODO: 在此處引用程序須要的其餘頭文件
#include <stdlib.h>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
#include <windows.h>
#include <assert.h>

#define itoa _itoa


const string& stringReplace(string& str, const string& strToReplace, const string& strReplaceTo);
char *stringReplace(char *str, const char *strToReplace, const char *strReplaceTo);

// UTF8編碼轉換到GBK編碼
int UTF8ToGBK(const char *lpUTF8Str, char *lpGBKStr,int nGBKStrLen);
//GBK編碼轉換到UTF8編碼
int GBKToUTF8(const char *lpGBKStr, char *lpUTF8Str, int nUTF8StrLen);


enum URL_TYPE{unkowurl, eastmoney, howbuy, fund123, jjmmw, qq};

getFundNetValue.cpp

// getFundNetValue.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"
#include <algorithm>
#include <string>
#include <map>
#include <ctime>
#include <algorithm>
#include "getRetracement.h"
using namespace std;


URL_TYPE getUrlType(const string& url)
{
    string urlLower = url;
    transform(urlLower.begin(), urlLower.end(), urlLower.begin(), ::tolower);
    if (urlLower.find("eastmoney.com") != string::npos || url.find("1234567.com") != string::npos )
    {
        return eastmoney;
    }
    else if (urlLower.find("howbuy.com") != string::npos)
    {
        return howbuy;
    }
    else if (urlLower.find("fund123.cn") != string::npos)
    {
        return fund123;
    }
    else if (urlLower.find("jjmmw.com") != string::npos)
    {
        return jjmmw;
    }
    else if (urlLower.find("qq.com") != string::npos)
    {
        return qq;
    }
    else
    {
        return unkowurl;
    }
}


size_t splitToVector(const char *src, const char *separator, vector<string>& vecOut)
{
    const char *pFound, *pBegin = src;
    const size_t nSeparatorLen = strlen(separator);

    vecOut.clear();

    while ( NULL != (pFound = strstr(pBegin, separator)) )
    {
        vecOut.push_back(string(pBegin, pFound));
        pBegin = pFound + nSeparatorLen;
    }

    if (*pBegin != '\0')
    {
        vecOut.push_back(pBegin);
    }

    return vecOut.size();
}

void removeCharHeadTail(string& str, const char trimChr)
{
    size_t begPos = 0;
    size_t endPos = string::npos;
    size_t nStrLen = str.size();

    if (nStrLen == 0)
    {
        return;
    }

    if (str[0] == trimChr) 
    {
        begPos = 1;
    }

    if (str[nStrLen - 1] == trimChr)
    {
        endPos = nStrLen - begPos - 1;
    }

    str = str.substr(begPos, endPos);
}

//獲取估值
size_t getFoundNetValueGz(const string& strHtml, vector<string>& vecResult, URL_TYPE urlType, /*const */map<string, string>& mpConfig)
{
    assert(strHtml.size() != 0);

    vecResult.clear();

    string strStart, strEnd;
    if (urlType == eastmoney || urlType == unkowurl)
    {
        strStart = "<input type=\"checkbox\"";
        strEnd = "</table>";
    }
    else if (urlType == fund123)
    {
        strStart = "<input type=checkbox";
        strEnd = "</table>";
    }
    else
    {
        fprintf(stderr, "未知網站\n");
        return 0;
    }


    //每一個基金數據從<tr id=
    size_t pos = strHtml.find(strStart, strHtml.find("增加率") + 1);

    bool endFlag = false;
    while (pos != string::npos && !endFlag)
    {
        size_t endpos = strHtml.find(strStart, pos + 1);

        //最後一個基金以後沒有<tr id=,因此須要另找結束標誌
        if (endpos == string::npos)
        {
            endFlag = true;

            endpos = strHtml.find(strEnd, pos + 1);
            if (endpos == string::npos)
            {
                break;
            }
        }

        char buffer[4096];
        size_t i = 0; 
        buffer[i++] = '\t';

        bool contentFlag = false;
        int nCnt = 0;
        for (i = 1; pos < endpos; pos++)
        {
            //沒有內容的節點不增長\t 如「<node></node>」
            if (strHtml[pos] == '<')
            {
                if (contentFlag && buffer[i - 1] != '\t')
                {
                    buffer[i++] = '\t';
                    nCnt++;
                }
                else if (urlType == fund123 && nCnt == 4)
                {
                    nCnt++;
                }

                contentFlag = false;
            }
            else if (strHtml[pos] == '>')
            {
                contentFlag = true;
            }
            else
            {
                if (!contentFlag) continue;

                if (strHtml[pos] == '\r' || strHtml[pos] == '\n')
                {
                    continue;
                }

                if (urlType != fund123 || nCnt != 4)
                {
                    buffer[i++] = strHtml[pos];

                    //除了每天基金網通常網站編碼爲utf8
                    if (urlType != eastmoney)
                    {
                        //utf8一個漢字佔三個字節 若發現目前處理的字符是中文字符首字節則需將後面兩個字節也一併處理了
                        if (strHtml[pos] & (1 << 7))
                        {
                            buffer[i++] = strHtml[++pos];
                            buffer[i++] = strHtml[++pos];
                        }
                    }
                }
            }
        }

        buffer[i] = '\0';
        char gbkBuffer[4096];
        //將utf8編碼轉成gbk編碼
        if( (urlType != eastmoney) 
            && !UTF8ToGBK(buffer, gbkBuffer, sizeof(gbkBuffer)) )
        {
            fprintf(stderr, "網頁編碼轉換失敗,請發郵件通知做者更新軟件:hch1986@21cn.com\n");
            vecResult.clear();
            break;
        }

        if (urlType == eastmoney)
        {
            //爲了保持與基金淨值格式相同,特地去掉這段文字
            vecResult.push_back(stringReplace(buffer, "估算圖\t", ""));
        }
        else
        {
            vecResult.push_back( stringReplace(gbkBuffer, "\t購買\t", "\t") );
        }
    }


    return vecResult.size();
}


//獲取基金網淨值數據
size_t getFoundNetValue(const string& strHtml, vector<string>& vecResult, URL_TYPE urlType, /*const */map<string, string>& mpConfig)
{
    string strNetValue;
    assert(strHtml.size() != 0);

    vecResult.clear();

    string strStart = mpConfig["allNetValueBegin"], strEnd = mpConfig["allNetValueEnd"];
    const size_t begPos = strHtml.find(strStart);
    const size_t endPos = strHtml.find(strEnd);
    char bufferResult[2046];
    char gbkBuffer[1024]; 

    if ((begPos == string::npos))
    {
        fprintf(stderr, "find strStart err\n");
        return 0;
    }

    strNetValue = strHtml.substr(begPos + strStart.size());

    vector<string> vecFunds;
    vector<string> vecCells;
    if (0 == splitToVector(strNetValue.c_str(), mpConfig["netValueSplit"].c_str(), vecFunds))
    {
        fprintf(stderr, "split funds err\n");
        return 0;
    }

    size_t todayNetValuePos = atoi(mpConfig["todayNetValuePos"].c_str());
    size_t yesterdayNetValuePos = atoi(mpConfig["yesterdayNetValuePos"].c_str());
    size_t increasePos = atoi(mpConfig["increasePos"].c_str());
    size_t fundSnPos = atoi(mpConfig["fundSnPos"].c_str());
    size_t fundNamePos = atoi(mpConfig["fundNamePos"].c_str());
    size_t maxPos = max(todayNetValuePos, max(yesterdayNetValuePos, max(increasePos, max(fundSnPos, fundNamePos))));
    bool bUtf8Flag = (mpConfig["utf2gbk"] == "1");
    bool bRemoveHeadTailFlag = (mpConfig["trimChar"].size() != 0);
    const char *fundName;

    for (size_t i = 0; i < vecFunds.size(); i++)
    {
        if (maxPos > splitToVector(vecFunds[i].c_str(), mpConfig["cellSplit"].c_str(), vecCells))
        {
            fprintf(stderr, "split cells err %d\n", (int)i);
            continue;
        }

        if (bRemoveHeadTailFlag)
        {
            for (size_t j = 0; j < vecCells.size(); j++)
            {
                removeCharHeadTail(vecCells[j], mpConfig["trimChar"][0]);
            }
        }

        if (mpConfig["leftTrimNameChar"].size() != 0)
        {
            string& fundName = vecCells[fundNamePos - 1];
            size_t pos = fundName.find(mpConfig["leftTrimNameChar"]);
            if (pos != string::npos)
            {
                fundName = fundName.substr(pos + mpConfig["leftTrimNameChar"].size());
            }
        }

        if (vecCells[increasePos - 1].size() == 0)
        {
            vecCells[increasePos - 1] = "0";
        }

        if (mpConfig["increaseIsPercent"] != "1")
        {
            //char valueBuf[32];
            //sprintf(valueBuf, "%.2f%%", atof(vecCells[increasePos - 1].c_str()) * 100);
            //vecCells[increasePos - 1] = valueBuf;
            vecCells[increasePos - 1] = vecCells[increasePos - 1] + "%";
        }

//         for (size_t j = 0; j < vecCells.size(); j++)
//         {
//             printf("%s ", vecCells[j].c_str());
//         }
//         printf("\n");
        fundName = vecCells[fundNamePos - 1].c_str();
        if (bUtf8Flag)
        {
            UTF8ToGBK(fundName, gbkBuffer, sizeof(gbkBuffer));
            fundName = gbkBuffer;
        }

        sprintf(bufferResult, "\t%d    %s    %s    ---    ---    %s    今日累計淨值    %s    昨日累計淨值    淨值增加值    %s",
            (int) i, vecCells[fundSnPos - 1].c_str(), fundName,
            vecCells[todayNetValuePos - 1].c_str(), vecCells[yesterdayNetValuePos - 1].c_str(), vecCells[increasePos - 1].c_str());
        vecResult.push_back(bufferResult);
    }

    return vecResult.size();
}




bool setClipboard(const string& str)
{
    if (!OpenClipboard(NULL))   
    {
        return false;
    }

    size_t nCount = str.size();  
    HGLOBAL hGlobalMem = GlobalAlloc(GMEM_MOVEABLE, (nCount+1) * sizeof(TCHAR));

    if (!hGlobalMem)  
    {   
        CloseClipboard();   
        return false;  
    }

    EmptyClipboard();  

    LPTSTR lpszStr= (LPTSTR) GlobalLock(hGlobalMem);  
    memcpy(lpszStr, str.c_str(), nCount * sizeof(TCHAR));  
    lpszStr[nCount] = (TCHAR) 0;  
    GlobalUnlock(hGlobalMem);

#ifdef UNICODE  
    SetClipboardData(CF_UNICODETEXT, hGlobalMem);
#else  
    SetClipboardData(CF_TEXT, hGlobalMem);
#endif  

    CloseClipboard();

    return true;
}

void testGetRectracement();

int readFileToStr(const char *strFilePath, string& strHtml)
{
    char buffer[4097];
    size_t nRead; 

    FILE *fp = fopen(strFilePath, "r");
    if (fp == NULL)
    {
        perror("沒法打開臨時文件,請確認文件夾是否只讀。");
        return -1;
    }

    strHtml = "";
    while ((nRead = fread(buffer, 1, sizeof(buffer) - 1, fp)) != 0)
    {
        buffer[nRead] = '\0';
        strHtml += buffer;
    }
    fclose(fp); 

    if (strHtml == "")
    {
        fprintf(stderr, "獲取基金淨值失敗,請檢查是否網絡鏈接有問題。\n");
        return -2;
    }

    return 0;
}

const string getValue(const string& strIn)
{
    size_t nPos = strIn.find(":\"");
    if (nPos == string::npos)
    {
        return "";
    }

    string res = strIn.substr(nPos + 2);

    if (res.size() <= 1 || res[res.size() - 1] != '\"')
    {
        return "";
    }

    res.resize(res.size() - 1);
    return res;
}

int parseHtml(const string &strHtml, map<string, string>& mpConfig, vector<NetValueInfo>& vecNetValueInfo)
{
    string strNetValue;
    assert(strHtml.size() != 0);

    string strStart = mpConfig["allNetValueBegin"], strEnd = mpConfig["allNetValueEnd"];
    const size_t begPos = strHtml.find(strStart);
    const size_t endPos = strHtml.find(strEnd);

    if ((begPos == string::npos))
    {
        fprintf(stderr, "find strStart err\n");
        return -1;
    }

    strNetValue = strHtml.substr(begPos + strStart.size());

    if (strNetValue.size() == 0)
    {
        return -5;
    }

    vector<string> vecFunds;
    vector<string> vecCells;
    if (0 == splitToVector(strNetValue.c_str(), mpConfig["netValueSplit"].c_str(), vecFunds))
    {
        fprintf(stderr, "split funds err\n");
        return -10;
    }

    size_t todayNetValuePos = atoi(mpConfig["todayNetValuePos"].c_str());
    size_t yesterdayNetValuePos = atoi(mpConfig["yesterdayNetValuePos"].c_str());
    size_t increasePos = atoi(mpConfig["increasePos"].c_str());
    size_t fundSnPos = atoi(mpConfig["fundSnPos"].c_str());
    size_t fundNamePos = atoi(mpConfig["fundNamePos"].c_str());
    size_t maxPos = max(todayNetValuePos, max(yesterdayNetValuePos, max(increasePos, max(fundSnPos, fundNamePos))));
    bool bUtf8Flag = (mpConfig["utf2gbk"] == "1");
    bool bRemoveHeadTailFlag = (mpConfig["trimChar"].size() != 0);


    for (size_t i = 0; i < vecFunds.size(); i++)
    {
        if (maxPos > splitToVector(vecFunds[i].c_str(), mpConfig["cellSplit"].c_str(), vecCells))
        {
            fprintf(stderr, "split cells err %d\n", (int)i);
            continue;
        }

        NetValueInfo netValue;
        netValue.strNetValueDate = getValue(vecCells[todayNetValuePos]);
        string val = getValue(vecCells[yesterdayNetValuePos]);
        if (val.size() == 0)
        {
            //若是累計淨值爲空,則取單日淨值 若是仍是空,則報錯
            //典型例子:中信保誠穩鴻A (006011)
            val = getValue(vecCells[yesterdayNetValuePos - 1]);
            if (val.size() == 0)
            {
                return -20;
            }
        }

        netValue.dAccumulatedNet = atof(val.c_str());
        vecNetValueInfo.push_back(netValue);
    }

    return 0;
}

#define DATE_LEN 8
#define YEAR_LEN 4
string  getCurrentTimeStr()
{
    time_t t = time(NULL);
    char ch[64] = {0};
    strftime(ch, sizeof(ch) - 1, "%Y%m%d%H%M%S", localtime(&t));
    return ch;
}

typedef struct tagNetValueInfoStartEnd
{
    tagNetValueInfoStartEnd(const string& y, size_t b, size_t e)
    {
        year = y;
        beg = b;
        end = e;
    }
    string year;
    size_t beg;
    size_t end;
}NetValueInfoStartEnd;

int _tmain(int argc, _TCHAR* argv[])
{

    //testGetRectracement();

    vector<string> vecResult;
    string strFundCode;
    string strCmdFormat;
    char cmd[1024];
    strCmdFormat = "curl \"http://api.fund.eastmoney.com/f10/lsjz?callback=jQuery183018519977574130597_1558194911277&fundCode=%s&pageIndex=1&pageSize=300&startDate=%s&endDate=%s&_=1558194929451\"  --referer \"http://fundf10.eastmoney.com/\"  --user-agent \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\" -o fund.html";

    if (argc > 1)
    {
        strFundCode = argv[1];
    }
    else
    {
        cerr << "請輸入6位基金代碼並按回車" << endl;
        cin >> strFundCode;
    }

    //讀取配置
    map<string, string> mpConfig;
    char nbuffer[1024];
    char *keyWord[] = {"url", "utf2gbk", "allNetValueBegin", "allNetValueEnd", "netValueSplit",
        "cellSplit", "todayNetValuePos", "yesterdayNetValuePos", "increasePos", "increaseIsPercent", 
        "fundSnPos", "fundNamePos", "trimChar", "leftTrimNameChar", "sleepSecond"};
    string strUrlName = "eastmoney";

    for (size_t i = 0; i < sizeof(keyWord) / sizeof(keyWord[0]); i++)
    {
        memset(nbuffer, 0, sizeof(nbuffer));
        GetPrivateProfileString(strUrlName.c_str(), 
            keyWord[i], 
            "", 
            nbuffer, 
            sizeof(nbuffer), 
            "./found.ini");
        fprintf(stderr, "[%s] = [%s]\n", keyWord[i], nbuffer);
        mpConfig[keyWord[i]] = nbuffer;
    }


    string strHtml = "";
    bool bSuccFlag = false;

    int nYear = atoi(getCurrentTimeStr().substr(0, YEAR_LEN).c_str());
    vector<NetValueInfo> vecNetValueInfo;
    vector<NetValueInfoStartEnd> vecPosInfo;

    for (int i = 0; i < 1000; ++i)
    {
        char tmpbuf[64];
        itoa(nYear - i, tmpbuf, 10);

        string strYear = tmpbuf;
        string strStartDate = strYear + "-01-01";
        string strEndDate = strYear + "-12-31";


        cout << "獲取" << strYear << "年淨值。基金代碼:" << strFundCode << endl;

        sprintf(cmd, strCmdFormat.c_str(), 
            strFundCode.c_str(), strStartDate.c_str(), strEndDate.c_str());
        if ( system(cmd) != 0 )
        {
            perror("curl調用失敗");
            return -3;
        }

        readFileToStr("fund.html", strHtml);
        size_t begPos = vecNetValueInfo.size();
        if (parseHtml(strHtml, mpConfig, vecNetValueInfo))
        {
            break;
        }

        bSuccFlag = true;
        size_t endPos = vecNetValueInfo.size();
        vecPosInfo.push_back(NetValueInfoStartEnd(strYear, begPos, endPos));

        /*
        vector<NetValueInfo> vecNetValueInfoNew = vecNetValueInfo;
        ResultInfo resultInfo;
        size_t nBegPos = 0, nEndPos = vecNetValueInfoNew.size();

        reverse(vecNetValueInfoNew.begin(),vecNetValueInfoNew.end());
        getRectracement(vecNetValueInfoNew, nBegPos, nEndPos, resultInfo);

        cout << "分析起止日期:[" << vecNetValueInfoNew[nBegPos].strNetValueDate << "," << vecNetValueInfoNew[nEndPos - 1].strNetValueDate << "]" << endl;
        resultInfo.print();
        */
        cout << "休息一會再獲取下一年淨值" << endl;
        Sleep(atoi(mpConfig["sleepSecond"].c_str()) * 1000);
    }

    if (!bSuccFlag)
    {
        cerr << "獲取基金淨值失敗" << endl;
        return -30;
    }

    if (vecPosInfo.size() > 1)
    {
        vecPosInfo.push_back(NetValueInfoStartEnd("all", 0, vecNetValueInfo.size()));
    }
        
    bSuccFlag = false;


    FILE *fp = fopen((strFundCode + ".txt").c_str(), "w");
    if (fp != NULL)
    {
        for (size_t i = 0; i < vecNetValueInfo.size(); i++)
        {
            fprintf(fp, "%s %f\n", vecNetValueInfo[i].strNetValueDate.c_str(), vecNetValueInfo[i].dAccumulatedNet);
        }

        fclose(fp);
    }

    strHtml = "";
    for (size_t i = 0; i < vecPosInfo.size(); i++)
    {
        vector<NetValueInfo> vecNetValueInfoNew;
        ResultInfo resultInfo;
        size_t nBegPos = 0, nEndPos;
        
        vecNetValueInfoNew.assign(vecNetValueInfo.begin() + vecPosInfo[i].beg,
            vecNetValueInfo.begin() + vecPosInfo[i].end);
        reverse(vecNetValueInfoNew.begin(),vecNetValueInfoNew.end());
        nEndPos = vecNetValueInfoNew.size();

        getRectracement(vecNetValueInfoNew, nBegPos, nEndPos, resultInfo);

        _snprintf(nbuffer, sizeof(nbuffer), "分析起止日期:[%s,%s]\n", 
            vecNetValueInfoNew[nBegPos].strNetValueDate.c_str(), vecNetValueInfoNew[nEndPos - 1].strNetValueDate.c_str());

        cout << nbuffer;
        resultInfo.print();
        strHtml += nbuffer;
        strHtml += resultInfo.getString();
    }

    fp = fopen((strFundCode + "_trace.txt").c_str(), "w");
    if (fp != NULL)
    {

        fprintf(fp, "%s\n", strHtml.c_str());

        fclose(fp);
    }

    //第二個參數爲1時才寫入剪貼板
    //if (argc > 2 && argv[2][0] == '1')
    {
        if (!setClipboard(strHtml))
        {
            perror("寫入剪貼板失敗,請手動複製");
            system("run fund.txt");
            return -5;
        }

        cout << "結果已複製到剪貼板,基金詳細淨值見程序目錄「基金代碼.txt」" << endl;
    }

    return 0;
}

 

 getRetracement.cpp

#include "stdafx.h"
#include "getRetracement.h"


int getRectracement(const vector<NetValueInfo> &vecNetValueInfo, size_t nBegPos, size_t nEndPos,
    OUT ResultInfo& resultInfo)
{
    string strTmp;

    double dLastNetValue = vecNetValueInfo[nBegPos].dAccumulatedNet; //記錄上一交易日基金淨值
    double dMinNetValue = dLastNetValue, dMaxNetValue = dLastNetValue; //記錄已出現最大和最小淨值
    size_t nMinPos = 0, nMaxPos = 0; //記錄已出現最大和最小淨值出現位置

    size_t nCurrRetraceDays = 0; //記錄當前連續回撤天數
    bool bIsRetraceLast = false; //上一交易日是否回撤
    size_t nMaxContinuousRetraceDays = 0; //最大連續回撤天數
    double dMaxContinuousRetrace = 0; //最大連續回撤率
    double dMaxRetrace = 0; //最大回撤率
    size_t nBegPosRetrace = 0; //記錄開始回撤的位置
    double dMaxRetraceFromBeg = 0; //從起始位置起算最大回撤率

    size_t nCurrRiseDays = 0; //記錄當前連續回撤天數
    bool bIsRiseLast = false; //上一交易日是否回撤
    size_t nMaxContinuousRiseDays = 0; //最大連續回撤天數
    double dMaxContinuousRise = 0; //最大連續回撤率
    double dMaxRise = 0; //最大回撤率
    size_t nBegPosRise = 0; //記錄開始回撤的位置
    double dMaxRiseFromBeg = 0; //從起始位置起算最大回撤率

    double dFirstValue = dLastNetValue; //第一天累計淨值

    size_t nRiseTimes = 0;
    size_t nMaxRiseTimes = 0;

    char tmpBuf[256];
    double dTmp;

    const size_t nLastPos = nEndPos - 1;
    resultInfo.resRetraceRiseFromBeginInfo.strBegDate = vecNetValueInfo[nBegPos].strNetValueDate;
    resultInfo.resRetraceRiseFromBeginInfo.dBegNetValue = vecNetValueInfo[nBegPos].dAccumulatedNet;
    sprintf(tmpBuf, "%.2f%%", (vecNetValueInfo[nLastPos].dAccumulatedNet - vecNetValueInfo[nBegPos].dAccumulatedNet) / vecNetValueInfo[nBegPos].dAccumulatedNet * 100);
    resultInfo.resRetraceRiseFromBeginInfo.strValue = tmpBuf;
    resultInfo.resRetraceRiseFromBeginInfo.strEndDate = vecNetValueInfo[nLastPos].strNetValueDate;
    resultInfo.resRetraceRiseFromBeginInfo.dEndNetValue = vecNetValueInfo[nLastPos].dAccumulatedNet;



    resultInfo.resMaxRetraceFromBeginInfo.strEndDate = resultInfo.resMaxRetraceFromBeginInfo.strBegDate = vecNetValueInfo[nBegPos].strNetValueDate;
    resultInfo.resMaxRetraceFromBeginInfo.dEndNetValue = resultInfo.resMaxRetraceFromBeginInfo.dBegNetValue = vecNetValueInfo[nBegPos].dAccumulatedNet;
    resultInfo.resMaxRetraceFromBeginInfo.strValue = "0.00%";

    resultInfo.resMaxRiseFromBeginInfo.strBegDate = vecNetValueInfo[nBegPos].strNetValueDate;
    resultInfo.resMaxRiseFromBeginInfo.dBegNetValue = vecNetValueInfo[nBegPos].dAccumulatedNet;

    for (size_t i = nBegPos + 1; i < nEndPos; ++i)
    {
        double dCurrNetValue = vecNetValueInfo[i].dAccumulatedNet;
        //從起始時間算最大回撤率
        if (dCurrNetValue < dFirstValue)
        {
            //看從起始時間起算最大回撤率是否須要更新
            dTmp = (dFirstValue - dCurrNetValue)/dFirstValue;
            if (dMaxRetraceFromBeg < dTmp)
            {
                dMaxRetraceFromBeg = dTmp;

                sprintf(tmpBuf, "-%.2f%%", dMaxRetraceFromBeg * 100);
                resultInfo.resMaxRetraceFromBeginInfo.strValue = tmpBuf;
                resultInfo.resMaxRetraceFromBeginInfo.strEndDate = vecNetValueInfo[i].strNetValueDate;
                resultInfo.resMaxRetraceFromBeginInfo.dEndNetValue = vecNetValueInfo[i].dAccumulatedNet;
            }
        }
        else if (dCurrNetValue > dFirstValue)
        {
            //看從起始時間起算最大上漲率是否須要更新
            dTmp = (dCurrNetValue - dFirstValue)/dFirstValue;
            if (dMaxRiseFromBeg < dTmp)
            {
                dMaxRiseFromBeg = dTmp;

                sprintf(tmpBuf, "%.2f%%", dMaxRiseFromBeg * 100);
                resultInfo.resMaxRiseFromBeginInfo.strValue = tmpBuf;
                resultInfo.resMaxRiseFromBeginInfo.strEndDate = vecNetValueInfo[i].strNetValueDate;
                resultInfo.resMaxRiseFromBeginInfo.dEndNetValue = vecNetValueInfo[i].dAccumulatedNet;
            }
        }

        //最大回撤率和上漲率
        {
            //出現最大值
            if (dCurrNetValue > dMaxNetValue)
            {
                //登記最大值
                dMaxNetValue = dCurrNetValue;
                nMaxPos = i;

            }

            //出現最小值
            if (dCurrNetValue < dMinNetValue)
            {
                //登記最小值
                dMinNetValue = dCurrNetValue;
                nMinPos = i;
            }

            //看最大回撤率是否須要更新
            dTmp = (dMaxNetValue - dCurrNetValue)/dMaxNetValue;
            if (dMaxRetrace < dTmp)
            {
                dMaxRetrace = dTmp;

                sprintf(tmpBuf, "-%.2f%%", dMaxRetrace * 100);
                resultInfo.resMaxRetraceInfo.strValue = tmpBuf;
                resultInfo.resMaxRetraceInfo.strBegDate = vecNetValueInfo[nMaxPos].strNetValueDate;
                resultInfo.resMaxRetraceInfo.dBegNetValue = vecNetValueInfo[nMaxPos].dAccumulatedNet;
                resultInfo.resMaxRetraceInfo.strEndDate = vecNetValueInfo[i].strNetValueDate;
                resultInfo.resMaxRetraceInfo.dEndNetValue = vecNetValueInfo[i].dAccumulatedNet;
            }

            //看最大上漲率是否須要更新
            dTmp = (dCurrNetValue - dMinNetValue)/dMinNetValue;
            if (dMaxRise < dTmp)
            {
                dMaxRise = dTmp;

                sprintf(tmpBuf, "%.2f%%", dMaxRise * 100);
                resultInfo.resMaxRiseInfo.strValue = tmpBuf;
                resultInfo.resMaxRiseInfo.strBegDate = vecNetValueInfo[nMinPos].strNetValueDate;
                resultInfo.resMaxRiseInfo.dBegNetValue = vecNetValueInfo[nMinPos].dAccumulatedNet;
                resultInfo.resMaxRiseInfo.strEndDate = vecNetValueInfo[i].strNetValueDate;
                resultInfo.resMaxRiseInfo.dEndNetValue = vecNetValueInfo[i].dAccumulatedNet;
            }
        }

        //最大和最長連續回撤率
        {
            //看連續回撤率是否須要更新
            //昨天淨值大於等於今天淨值 認爲是回撤
            if (dLastNetValue >= dCurrNetValue)
            {
                if (bIsRetraceLast)
                {
                    ++nCurrRetraceDays;
                }
                else
                {
                    //開始第一天回撤
                    bIsRetraceLast = true;
                    nCurrRetraceDays = 1;
                    nBegPosRetrace = i - 1;
                }
            }
            else
            {
                //回撤結束
                if (bIsRetraceLast)
                {
                    bIsRetraceLast = false;
                    //最大連續回撤天數
                    size_t nEndPosRetrace = i - 1;
                    if (nMaxContinuousRetraceDays < nCurrRetraceDays)
                    {
                        nMaxContinuousRetraceDays = nCurrRetraceDays;
                        resultInfo.resMaxContinRetraceDaysInfo.strValue = itoa(nCurrRetraceDays, tmpBuf, 10);
                        resultInfo.resMaxContinRetraceDaysInfo.strBegDate = vecNetValueInfo[nBegPosRetrace].strNetValueDate;
                        resultInfo.resMaxContinRetraceDaysInfo.dBegNetValue = vecNetValueInfo[nBegPosRetrace].dAccumulatedNet;
                        resultInfo.resMaxContinRetraceDaysInfo.strEndDate = vecNetValueInfo[nEndPosRetrace].strNetValueDate;
                        resultInfo.resMaxContinRetraceDaysInfo.dEndNetValue = vecNetValueInfo[nEndPosRetrace].dAccumulatedNet;
                    }

                    //最大連續回撤率
                    dTmp = 
                        (vecNetValueInfo[nBegPosRetrace].dAccumulatedNet - vecNetValueInfo[nEndPosRetrace].dAccumulatedNet) / vecNetValueInfo[nBegPosRetrace].dAccumulatedNet;
                    if (dMaxContinuousRetrace < dTmp)
                    {
                        dMaxContinuousRetrace = dTmp;
                        sprintf(tmpBuf, "-%.2f%%", dMaxContinuousRetrace * 100);
                        resultInfo.resMaxContinRetraceInfo.strValue = tmpBuf;

                        resultInfo.resMaxContinRetraceInfo.strBegDate = vecNetValueInfo[nBegPosRetrace].strNetValueDate;
                        resultInfo.resMaxContinRetraceInfo.dBegNetValue = vecNetValueInfo[nBegPosRetrace].dAccumulatedNet;
                        resultInfo.resMaxContinRetraceInfo.strEndDate = vecNetValueInfo[nEndPosRetrace].strNetValueDate;
                        resultInfo.resMaxContinRetraceInfo.dEndNetValue = vecNetValueInfo[nEndPosRetrace].dAccumulatedNet;
                    }
                }
            }

            //看連續上漲率是否須要更新
            //昨天淨值小於等於今天淨值 認爲是上漲
            if (dLastNetValue <= dCurrNetValue)
            {
                if (bIsRiseLast)
                {
                    ++nCurrRiseDays;
                }
                else
                {
                    //開始第一天上漲
                    bIsRiseLast = true;
                    nCurrRiseDays = 1;
                    nBegPosRise = i - 1;
                }
            }
            else
            {
                //上漲結束
                if (bIsRiseLast)
                {
                    bIsRiseLast = false;
                    //最大連續上漲天數
                    size_t nEndPosRise = i - 1;
                    if (nMaxContinuousRiseDays < nCurrRiseDays)
                    {
                        nMaxContinuousRiseDays = nCurrRiseDays;
                        resultInfo.resMaxContinRiseDaysInfo.strValue = itoa(nCurrRiseDays, tmpBuf, 10);
                        resultInfo.resMaxContinRiseDaysInfo.strBegDate = vecNetValueInfo[nBegPosRise].strNetValueDate;
                        resultInfo.resMaxContinRiseDaysInfo.dBegNetValue = vecNetValueInfo[nBegPosRise].dAccumulatedNet;
                        resultInfo.resMaxContinRiseDaysInfo.strEndDate = vecNetValueInfo[nEndPosRise].strNetValueDate;
                        resultInfo.resMaxContinRiseDaysInfo.dEndNetValue = vecNetValueInfo[nEndPosRise].dAccumulatedNet;
                    }

                    //最大連續上漲率
                    dTmp = 
                        (vecNetValueInfo[nEndPosRise].dAccumulatedNet - vecNetValueInfo[nBegPosRise].dAccumulatedNet) / vecNetValueInfo[nBegPosRise].dAccumulatedNet;
                    if (dMaxContinuousRise < dTmp)
                    {
                        dMaxContinuousRise = dTmp;
                        sprintf(tmpBuf, "%.2f%%", dMaxContinuousRise * 100);
                        resultInfo.resMaxContinRiseInfo.strValue = tmpBuf;

                        resultInfo.resMaxContinRiseInfo.strBegDate = vecNetValueInfo[nBegPosRise].strNetValueDate;
                        resultInfo.resMaxContinRiseInfo.dBegNetValue = vecNetValueInfo[nBegPosRise].dAccumulatedNet;
                        resultInfo.resMaxContinRiseInfo.strEndDate = vecNetValueInfo[nEndPosRise].strNetValueDate;
                        resultInfo.resMaxContinRiseInfo.dEndNetValue = vecNetValueInfo[nEndPosRise].dAccumulatedNet;
                    }
                }
            }
        }

        dLastNetValue = dCurrNetValue;
    }

    //resultInfo.print();

    return 0;
}


void testGetRectracement()
{
    vector<NetValueInfo> vecNetValueInfo;

    //vecNetValueInfo.push_back(NetValueInfo("2019-01-01", 3.8184));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-02", 3.6184));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-03", 3.5699));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-04", 3.6276));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-07", 3.6417));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-08", 3.6464));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-09", 3.7029));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-10", 3.6864));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-11", 3.7087));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-14", 3.6527));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-15", 3.7808));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-16", 3.7773));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-17", 3.7721));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-18", 3.8566));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-21", 3.8955));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-22", 3.8324));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-23", 3.8182));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-24", 3.839));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-25", 3.8749));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-28", 3.9008));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-29", 3.9123));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-30", 3.8597));
    vecNetValueInfo.push_back(NetValueInfo("2019-01-31", 3.9123));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-01", 3.9763));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-11", 4.0967));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-12", 4.116));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-13", 4.169));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-14", 4.2157));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-15", 4.1325));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-18", 4.2385));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-19", 4.2204));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-20", 4.2307));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-21", 4.2244));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-22", 4.262));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-25", 4.3901));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-26", 4.3299));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-27", 4.3243));
    vecNetValueInfo.push_back(NetValueInfo("2019-02-28", 4.3617));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-01", 4.4804));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-04", 4.5156));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-05", 4.5079));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-06", 4.4816));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-07", 4.3819));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-08", 4.294));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-11", 4.4107));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-12", 4.3957));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-13", 4.4));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-14", 4.4248));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-15", 4.5086));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-18", 4.6918));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-19", 4.655));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-20", 4.6547));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-21", 4.6234));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-22", 4.6526));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-25", 4.5371));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-26", 4.5402));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-27", 4.6341));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-28", 4.6845));
    vecNetValueInfo.push_back(NetValueInfo("2019-03-29", 4.8498));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-01", 4.9265));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-02", 4.8873));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-03", 4.8872));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-04", 4.9498));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-08", 4.9596));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-09", 5.0425));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-10", 5.1024));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-11", 4.9415));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-12", 4.921));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-15", 4.8815));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-16", 4.9717));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-17", 4.9893));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-18", 4.9827));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-19", 5.0434));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-22", 4.9983));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-23", 5.0508));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-24", 5.0265));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-25", 4.9531));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-26", 4.9318));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-29", 5.0159));
    vecNetValueInfo.push_back(NetValueInfo("2019-04-30", 5.0403));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-06", 4.7775));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-07", 4.8874));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-08", 4.831));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-09", 4.6981));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-10", 4.9068));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-13", 4.8639));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-14", 4.8238));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-15", 5.0074));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-16", 5.0627));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-17", 4.9514));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-20", 4.8840));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-21", 4.9350));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-22", 4.9049));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-23", 4.8145));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-24", 4.8299));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-27", 4.8482));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-28", 4.9015));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-29", 4.8845));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-30", 4.8609));
    vecNetValueInfo.push_back(NetValueInfo("2019-05-31", 4.8433));

    ResultInfo resultInfo;
    size_t nBegPos = 58, nEndPos = vecNetValueInfo.size();
    getRectracement(vecNetValueInfo, nBegPos, nEndPos, resultInfo);

    cout << "分析起止日期:[" << vecNetValueInfo[nBegPos].strNetValueDate << "," << vecNetValueInfo[nEndPos - 1].strNetValueDate << "]" << endl;
    resultInfo.print();
}

stdafx.cpp

// stdafx.cpp : 只包括標準包含文件的源文件
// getFundNetValue.pch 將做爲預編譯頭
// stdafx.obj 將包含預編譯類型信息

#include "stdafx.h"

// TODO: 在 STDAFX.H 中
// 引用任何所需的附加頭文件,而不是在此文件中引用

const string& stringReplace(string& str, const string& strToReplace, const string& strReplaceTo)
{
    size_t pos = str.find(strToReplace);
    if (pos != string::npos)
    {
        str.replace(pos, strToReplace.size(), strReplaceTo);
    }

    return str;
}

char *stringReplace(char *str, const char *strToReplace, const char *strReplaceTo)
{
    size_t cntOld = strlen(strToReplace);
    size_t cntNew = strlen(strReplaceTo);

    if (cntNew == cntOld && strcmp(strReplaceTo, strToReplace) == 0)
    {
        return str;
    }

    char *p = strstr(str, strToReplace);
    if (p == NULL)
    {
        return str;
    }

    if (cntOld < cntNew)
    {
        //先找到原字符串尾部\0的位置
        char *endpos = p;
        while(*endpos)
        {
            endpos++;
        }

        //將字符串從尾部起,每一個字符向後移動cntNew - cntOld個位置
        while (endpos >= p + cntOld)
        {
            *(endpos + cntNew - cntOld) = *endpos;
            endpos--;
        }

        memcpy(p, strReplaceTo, cntNew);

    }
    else if (cntNew == cntOld)
    {
        memcpy(p, strReplaceTo, cntNew);
    }
    else
    {
        memcpy(p, strReplaceTo, cntNew);
        memmove(p + cntNew, p + cntOld, strlen(p + cntOld) + 1);
    }

    return str;
}

//GBK編碼轉換到UTF8編碼
int GBKToUTF8(const char *lpGBKStr, char *lpUTF8Str, int nUTF8StrLen)
{
    wchar_t *lpUnicodeStr = NULL;
    int nRetLen = 0;

    if(!lpGBKStr)  //若是GBK字符串爲NULL則出錯退出
        return 0;

    nRetLen = ::MultiByteToWideChar(CP_ACP, 0, lpGBKStr, -1, NULL, NULL);  //獲取轉換到Unicode編碼後所須要的字符空間長度
    lpUnicodeStr = new WCHAR[nRetLen + 1];  //爲Unicode字符串空間
    nRetLen = ::MultiByteToWideChar(CP_ACP, 0, lpGBKStr, -1, lpUnicodeStr, nRetLen);  //轉換到Unicode編碼
    if(!nRetLen)  //轉換失敗則出錯退出
        return 0;

    nRetLen = ::WideCharToMultiByte(CP_UTF8, 0, lpUnicodeStr, -1, NULL, 0, NULL, NULL);  //獲取轉換到UTF8編碼後所須要的字符空間長度

    if(!lpUTF8Str)  //輸出緩衝區爲空則返回轉換後須要的空間大小
    {
        if(lpUnicodeStr)
            delete []lpUnicodeStr;
        return nRetLen;
    }

    if(nUTF8StrLen < nRetLen)  //若是輸出緩衝區長度不夠則退出
    {
        if(lpUnicodeStr)
            delete []lpUnicodeStr;
        return 0;
    }

    nRetLen = ::WideCharToMultiByte(CP_UTF8,0,lpUnicodeStr,-1,(char *)lpUTF8Str,nUTF8StrLen,NULL,NULL);  //轉換到UTF8編碼

    if(lpUnicodeStr)
        delete []lpUnicodeStr;

    return nRetLen;
}


// UTF8編碼轉換到GBK編碼
int UTF8ToGBK(const char *lpUTF8Str, char *lpGBKStr,int nGBKStrLen)
{
    wchar_t * lpUnicodeStr = NULL;
    int nRetLen = 0;

    if(!lpUTF8Str)  //若是UTF8字符串爲NULL則出錯退出
        return 0;

    nRetLen = ::MultiByteToWideChar(CP_UTF8, 0, lpUTF8Str, -1, NULL, NULL);  //獲取轉換到Unicode編碼後所須要的字符空間長度
    lpUnicodeStr = new WCHAR[nRetLen + 1];  //爲Unicode字符串空間
    nRetLen = ::MultiByteToWideChar(CP_UTF8,0,(char *)lpUTF8Str,-1,lpUnicodeStr,nRetLen);  //轉換到Unicode編碼
    if(!nRetLen)  //轉換失敗則出錯退出
        return 0;

    nRetLen = ::WideCharToMultiByte(CP_ACP,0,lpUnicodeStr,-1,NULL,NULL,NULL,NULL);  //獲取轉換到GBK編碼後所須要的字符空間長度

    if(!lpGBKStr)  //輸出緩衝區爲空則返回轉換後須要的空間大小
    {
        if(lpUnicodeStr)
            delete []lpUnicodeStr;
        return nRetLen;
    }

    if(nGBKStrLen < nRetLen)  //若是輸出緩衝區長度不夠則退出
    {
        if(lpUnicodeStr)
            delete []lpUnicodeStr;
        return 0;
    }

    nRetLen = ::WideCharToMultiByte(CP_ACP,0,lpUnicodeStr,-1,(char *)lpGBKStr,nRetLen,NULL,NULL);  //轉換到GBK編碼

    if(lpUnicodeStr)
        delete []lpUnicodeStr;

    return nRetLen;
}



char *stringReplaceAll(char *str, const char *strToReplace, const char *strReplaceTo)
{
    size_t cntOld = strlen(strToReplace);
    size_t cntNew = strlen(strReplaceTo);

    if (cntNew == cntOld && strcmp(strReplaceTo, strToReplace) == 0)
    {
        return str;
    }

    if (*strToReplace == '\0')
    {
        return str;
    }

    char *p = strstr(str, strToReplace);
    if (p == NULL)
    {
        return str;
    }

    vector<char *> vecStrPlace;
    while (p != NULL)
    {
        vecStrPlace.push_back(p);
        p = strstr(p + cntOld + 1, strToReplace);
    }

    if (cntOld < cntNew)
    {
        //取得還沒有移動的最後一個字符位置(包括\0)
        char *pEnd = p + strlen(p) + 1;
        size_t n = vecStrPlace.size();
        int nMove = n * (cntNew - cntOld);
        while (nMove > 0)
        {
            //取得還沒有移動的最後一個字符串起始位置
            char *p = vecStrPlace[n - 1] + cntOld;
            //將該位置起至最後一個須要移動的字符一塊兒日後移
            memmove(p, p + nMove, pEnd - p + 1);
            memcpy(p + nMove - cntNew, strReplaceTo, cntNew);
            pEnd = vecStrPlace[n - 1] - 1;
            nMove -= (cntNew - cntOld);
            n--;
        }    
    }
    else if (cntNew == cntOld)
    {
        for (size_t i = 0; i < vecStrPlace.size(); i++)
        {
            memcpy(vecStrPlace[i], strReplaceTo, cntNew);
        }
    }
    else
    {
        p = vecStrPlace[0];
        for (size_t i = 0; i < vecStrPlace.size() - 1; i++)
        {
            memcpy(p, strReplaceTo, cntNew);
            p += cntNew;
            memmove(p, vecStrPlace[i] + cntOld, vecStrPlace[i + 1] - vecStrPlace[i] - cntOld);
            p += vecStrPlace[i + 1] - vecStrPlace[i] - cntOld;
        }
        memcpy(p, strReplaceTo, cntNew);
        p += cntNew;
        memmove(p, vecStrPlace[vecStrPlace.size() - 1] + cntOld, strlen(vecStrPlace[vecStrPlace.size() - 1] + cntOld) + 1);
    }

    return str;
}
相關文章
相關標籤/搜索