From 75d0363f17314fcbe5f11cc46822dc6378326363 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 11 Mar 2022 22:35:40 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"KDataImp=E4=B8=8D=E5=86=8D=E6=9C=89?= =?UTF-8?q?=E5=A4=9A=E7=A7=8D=E5=AE=9E=E7=8E=B0=EF=BC=8C=E8=BF=9B=E4=B8=80?= =?UTF-8?q?=E6=AD=A5=E6=B8=85=E7=90=86=E5=90=88=E5=B9=B6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 866020e1c130b3b33466dc7745988a8a2bca065f. --- hikyuu_cpp/hikyuu/KData.cpp | 435 +-------------------------------- hikyuu_cpp/hikyuu/KData.h | 81 +++--- hikyuu_cpp/hikyuu/KDataImp.cpp | 387 +++++++++++++++++++++++++++++ hikyuu_cpp/hikyuu/KDataImp.h | 68 ++++++ 4 files changed, 504 insertions(+), 467 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/KDataImp.cpp create mode 100644 hikyuu_cpp/hikyuu/KDataImp.h diff --git a/hikyuu_cpp/hikyuu/KData.cpp b/hikyuu_cpp/hikyuu/KData.cpp index 5b7fc9ea..1297477d 100644 --- a/hikyuu_cpp/hikyuu/KData.cpp +++ b/hikyuu_cpp/hikyuu/KData.cpp @@ -5,11 +5,9 @@ * Author: fasiondog */ -#include -#include -#include #include "KData.h" #include "StockManager.h" +#include "KDataImp.h" #include "indicator/crt/KDATA.h" #include @@ -28,101 +26,15 @@ string KData::toString() const { return os.str(); } -KData::KData(const Stock& stock, const KQuery& query) -: m_query(query), m_raw_query(query), m_stock(stock), m_start(0), m_end(0) { - if (m_stock.isNull()) { - return; +KData::KData(const Stock& stock, const KQuery& query) { + if (!stock.isNull()) { + m_imp = KDataImpPtr(new KDataImp(stock, query)); } - - bool sucess = m_stock.getIndexRange(query, m_start, m_end); - m_query = KQueryByIndex(m_start, m_end, query.kType(), query.recoverType()); - if (!sucess) { - m_start = 0; - m_end = 0; - return; - } - - m_buffer = m_stock.getKRecordList(m_query); - - //不支持复权时,直接返回 - if (m_query.recoverType() == KQuery::NO_RECOVER) - return; - - //日线以上复权处理 - if (m_query.kType() == KQuery::WEEK || m_query.kType() == KQuery::MONTH || - m_query.kType() == KQuery::QUARTER || m_query.kType() == KQuery::HALFYEAR || - m_query.kType() == KQuery::YEAR) { - _recoverForUpDay(); - return; - } - - switch (m_query.recoverType()) { - case KQuery::NO_RECOVER: - // do nothing - break; - - case KQuery::FORWARD: - _recoverForward(); - break; - - case KQuery::BACKWARD: - _recoverBackward(); - break; - - case KQuery::EQUAL_FORWARD: - _recoverEqualForward(); - break; - - case KQuery::EQUAL_BACKWARD: - _recoverEqualBackward(); - break; - - default: - HKU_ERROR("Invalid RecvoerType!"); - return; - } -} - -KData::KData(const KData& x) -: m_buffer(x.m_buffer), - m_query(x.m_query), - m_raw_query(x.m_raw_query), - m_stock(x.m_stock), - m_start(x.m_start), - m_end(x.m_end) {} - -KData::KData(KData&& x) -: m_buffer(std::move(x.m_buffer)), - m_query(x.m_query), - m_raw_query(x.m_raw_query), - m_stock(std::move(x.m_stock)), - m_start(x.m_start), - m_end(x.m_end) {} - -KData& KData::operator=(const KData& x) { - HKU_IF_RETURN(this == &x, *this); - m_buffer = x.m_buffer; - m_stock = x.m_stock; - m_query = x.m_query; - m_raw_query = x.m_raw_query; - m_start = x.m_start; - m_end = x.m_end; - return *this; -} - -KData& KData::operator=(KData&& x) { - HKU_IF_RETURN(this == &x, *this); - m_buffer = std::move(x.m_buffer); - m_stock = std::move(x.m_stock); - m_query = x.m_query; - m_raw_query = x.m_raw_query; - m_start = x.m_start; - m_end = x.m_end; - return *this; } bool KData::operator==(const KData& thr) { - return this == &thr || (getStock() == thr.getStock() && getQuery() == thr.getQuery()); + return this == &thr || m_imp == thr.m_imp || + (getStock() == thr.getStock() && getQuery() == thr.getQuery()); } size_t KData::getPosInStock(Datetime datetime) const { @@ -130,341 +42,6 @@ size_t KData::getPosInStock(Datetime datetime) const { return pos == Null() ? Null() : pos + startPos(); } -size_t KData::getPos(const Datetime& datetime) const { - KRecordList::const_iterator iter; - KRecord comp_record; - comp_record.datetime = datetime; - boost::function f = - boost::bind(&KRecord::datetime, _1) < boost::bind(&KRecord::datetime, _2); - - iter = lower_bound(m_buffer.begin(), m_buffer.end(), comp_record, f); - if (iter == m_buffer.end() || iter->datetime != datetime) { - return Null(); - } - - return (iter - m_buffer.begin()); -} - -DatetimeList KData::getDatetimeList() const { - DatetimeList result; - if (empty()) { - return result; - } - result = getStock().getDatetimeList(KQuery(startPos(), lastPos() + 1, getQuery().kType())); - return result; -} - -size_t KData::expand(size_t num) { - HKU_IF_RETURN(m_stock.isNull() || m_end >= m_stock.getCount(m_query.kType()), 0); - KQuery query; - if (m_raw_query.queryType() == KQuery::INDEX) { - query = KQueryByIndex(m_end, m_end + num, m_raw_query.kType(), m_raw_query.recoverType()); - } else { - query = KQueryByDate(m_raw_query.endDatetime(), Datetime::max(), m_raw_query.kType(), - m_raw_query.recoverType()); - size_t start = 0, end = 0; - bool success = m_stock.getIndexRange(query, start, end); - HKU_IF_RETURN(!success, 0); - query = KQueryByIndex(start, start + num, m_raw_query.kType(), m_raw_query.recoverType()); - } - - KRecordList records = m_stock.getKRecordList(query); - HKU_IF_RETURN(records.empty(), 0); - - size_t count = 0; - for (auto& record : records) { - m_buffer.emplace_back(record); - count++; - } - m_end += count; - m_query = KQueryByIndex(m_start, m_end, m_query.kType(), m_query.recoverType()); - if (m_raw_query.queryType() == KQuery::DATE) { - if (size() > 0) { - m_raw_query = KQueryByDate(m_raw_query.startDatetime(), - m_buffer[size() - 1].datetime + TimeDelta(1), - m_raw_query.kType(), m_raw_query.recoverType()); - } - } else { - m_raw_query = m_query; - } - return count; -} - -void KData::_recoverForUpDay() { - HKU_IF_RETURN(empty(), void()); - std::function startOfPhase; - if (m_query.kType() == KQuery::WEEK) { - startOfPhase = &Datetime::startOfWeek; - } else if (m_query.kType() == KQuery::MONTH) { - startOfPhase = &Datetime::startOfMonth; - } else if (m_query.kType() == KQuery::QUARTER) { - startOfPhase = &Datetime::startOfQuarter; - } else if (m_query.kType() == KQuery::HALFYEAR) { - startOfPhase = &Datetime::startOfHalfyear; - } else if (m_query.kType() == KQuery::YEAR) { - startOfPhase = &Datetime::startOfYear; - } - - Datetime startDate = startOfPhase(m_buffer.front().datetime); - Datetime endDate = m_buffer.back().datetime.nextDay(); - KQuery query = KQueryByDate(startDate, endDate, KQuery::DAY, m_query.recoverType()); - KData day_list = m_stock.getKData(query); - if (day_list.empty()) - return; - - size_t day_pos = 0; - size_t day_total = day_list.size(); - size_t length = size(); - for (size_t i = 0; i < length; i++) { - Datetime phase_start_date = startOfPhase(m_buffer[i].datetime); - Datetime phase_end_date = m_buffer[i].datetime; - if (day_pos >= day_total) - break; - - while (day_list[day_pos].datetime < phase_start_date) { - day_pos++; - } - KRecord record = day_list[day_pos]; - int pre_day_pos = day_pos; - while (day_pos < day_total && day_list[day_pos].datetime <= phase_end_date) { - if (day_list[day_pos].lowPrice < record.lowPrice) { - record.lowPrice = day_list[day_pos].lowPrice; - } else if (day_list[day_pos].highPrice > record.highPrice) { - record.highPrice = day_list[day_pos].highPrice; - } - record.closePrice = day_list[day_pos].closePrice; - day_pos++; - } - if (pre_day_pos != day_pos) { - m_buffer[i].openPrice = record.openPrice; - m_buffer[i].highPrice = record.highPrice; - m_buffer[i].lowPrice = record.lowPrice; - m_buffer[i].closePrice = record.closePrice; - } - } - - return; -} - -/****************************************************************************** - * 前复权公式:复权后价格=[(复权前价格-现金红利)+配(新)股价格×流通股份变动比例]÷(1+流通股份变动比例) - * 向前复权指以除权后的股价为基准(即除权后的股价不变),将除权前的股价降下来。 - * 复权计算时首先从上市日开始,逐日向后判断,遇到除权日,则将上市日到除权日之间(不包括除权日)的 - * 全部股价通过复权计算降下来;然后再继续向后判断,遇到下一个除权日,则再次将上市日到该除权日之间 - * (不包括除权日)的全部股价通过复权计算降下来。 - *****************************************************************************/ -void KData::_recoverForward() { - size_t total = m_buffer.size(); - HKU_IF_RETURN(total == 0, void()); - - Datetime start_date(m_buffer.front().datetime.date()); - Datetime end_date(m_buffer.back().datetime.date() + bd::days(1)); - StockWeightList weightList = m_stock.getWeight(start_date, end_date); - StockWeightList::const_iterator weightIter = weightList.begin(); - StockWeightList::const_iterator pre_weightIter = weightIter; - - size_t pre_pos = 0; - for (; weightIter != weightList.end(); ++weightIter) { - //计算流通股份变动比例,但不处理仅仅只有流通股本改变的情况 - if ((weightIter->countAsGift() == 0.0 && weightIter->countForSell() == 0.0 && - weightIter->priceForSell() == 0.0 && weightIter->bonus() == 0.0 && - weightIter->increasement() == 0.0)) - continue; - - size_t i = pre_pos; - while (i < total && m_buffer[i].datetime < weightIter->datetime()) { - i++; - } - pre_pos = i; //除权日 - - price_t change = 0.1 * (weightIter->countAsGift() + weightIter->countForSell() + - weightIter->increasement()); - price_t denominator = 1.0 + change; //分母 = (1+流通股份变动比例) - price_t temp = weightIter->priceForSell() * change - 0.1 * weightIter->bonus(); - - if (denominator == 1.0 && temp == 0.0) - continue; - - for (i = 0; i < pre_pos; ++i) { - m_buffer[i].openPrice = - roundEx((m_buffer[i].openPrice + temp) / denominator, m_stock.precision()); - m_buffer[i].highPrice = - roundEx((m_buffer[i].highPrice + temp) / denominator, m_stock.precision()); - m_buffer[i].lowPrice = - roundEx((m_buffer[i].lowPrice + temp) / denominator, m_stock.precision()); - m_buffer[i].closePrice = - roundEx((m_buffer[i].closePrice + temp) / denominator, m_stock.precision()); - } - } -} - -/****************************************************************************** - * 后复权公式:复权后价格=复权前价格×(1+流通股份变动比例)-配(新)股价格×流通股份变动比例+现金红利 - * 向后复权指以除权前的股价为基准(即除权前的股价不变),将除权后的股价升上去。复权计算时首先从最新日开始, - * 逐日向前判断,遇到除权日,则将除权日到最新日之间(包括除权日)的全部股价通过复权计算升上去;然后再继续 - * 向前判断,遇到下一个除权日,则再次将除权日到最新日之间(包括除权日)的全部股价通过复权计算升上去。 - *****************************************************************************/ -void KData::_recoverBackward() { - size_t total = m_buffer.size(); - HKU_IF_RETURN(total == 0, void()); - - Datetime start_date(m_buffer.front().datetime.date()); - Datetime end_date(m_buffer.back().datetime.date() + bd::days(1)); - StockWeightList weightList = m_stock.getWeight(start_date, end_date); - StockWeightList::const_reverse_iterator weightIter = weightList.rbegin(); - StockWeightList::const_reverse_iterator pre_weightIter; - - size_t pre_pos = total - 1; - for (; weightIter != weightList.rend(); ++weightIter) { - //计算流通股份变动比例,但不处理仅仅只有流通股本改变的情况 - if ((weightIter->countAsGift() == 0.0 && weightIter->countForSell() == 0.0 && - weightIter->priceForSell() == 0.0 && weightIter->bonus() == 0.0 && - weightIter->increasement() == 0.0)) - continue; - - size_t i = pre_pos; - while (i > 0 && m_buffer[i].datetime > weightIter->datetime()) { - i--; - } - pre_pos = i; - - //流通股份变动比例 - price_t change = 0.1 * (weightIter->countAsGift() + weightIter->countForSell() + - weightIter->increasement()); - price_t denominator = 1.0 + change; //(1+流通股份变动比例) - price_t temp = 0.1 * weightIter->bonus() - weightIter->priceForSell() * change; - - if (denominator == 1.0 && temp == 0.0) - continue; - - for (i = pre_pos; i < total; ++i) { - m_buffer[i].openPrice = - roundEx(m_buffer[i].openPrice * denominator + temp, m_stock.precision()); - m_buffer[i].highPrice = - roundEx(m_buffer[i].highPrice * denominator + temp, m_stock.precision()); - m_buffer[i].lowPrice = - roundEx(m_buffer[i].lowPrice * denominator + temp, m_stock.precision()); - m_buffer[i].closePrice = - roundEx(m_buffer[i].closePrice * denominator + temp, m_stock.precision()); - } - } -} - -/****************************************************************************** - * 等比前复权公式:复权后价格=复权前价格*复权率 - * 复权率={[(股权登记日收盘价-现金红利)+配(新)股价格×流通股份变动比例]÷(1+流通股份变动比例)}÷股权登记日收盘价 - * 向前复权指以除权后的股价为基准(即除权后的股价不变),将除权前的股价降下来。 - * 复权计算时首先从上市日开始,逐日向后判断,遇到除权日,则将上市日到除权日之间(不包括除权日)的 - * 全部股价通过复权计算降下来;然后再继续向后判断,遇到下一个除权日,则再次将上市日到该除权日之间 - * (不包括除权日)的全部股价通过复权计算降下来。 - *****************************************************************************/ -void KData::_recoverEqualForward() { - size_t total = m_buffer.size(); - HKU_IF_RETURN(total == 0, void()); - - Datetime start_date(m_buffer.front().datetime.date()); - Datetime end_date(m_buffer.back().datetime.date() + bd::days(1)); - StockWeightList weightList = m_stock.getWeight(start_date, end_date); - if (weightList.empty()) { - return; - } - - KRecordList kdata = m_buffer; //防止同一天两条权息记录 - StockWeightList::const_iterator weightIter = weightList.begin(); - StockWeightList::const_iterator pre_weightIter; - size_t pre_pos = 0; - for (; weightIter != weightList.end(); ++weightIter) { - //计算流通股份变动比例,但不处理仅仅只有流通股本改变的情况 - if ((weightIter->countAsGift() == 0.0 && weightIter->countForSell() == 0.0 && - weightIter->priceForSell() == 0.0 && weightIter->bonus() == 0.0 && - weightIter->increasement() == 0.0)) - continue; - - size_t i = pre_pos; - while (i < total && m_buffer[i].datetime < weightIter->datetime()) { - i++; - } - pre_pos = i; //除权日 - - //股权登记日(即除权日的前一天数据)收盘价 - if (pre_pos == 0) { - continue; - } - price_t closePrice = kdata[pre_pos - 1].closePrice; - if (closePrice == 0.0) { - continue; //除零保护 - } - - //流通股份变动比例 - price_t change = 0.1 * (weightIter->countAsGift() + weightIter->countForSell() + - weightIter->increasement()); - price_t denominator = 1.0 + change; //(1+流通股份变动比例) - price_t temp = weightIter->priceForSell() * change - 0.1 * weightIter->bonus(); - - if (denominator == 0.0 || (denominator == 1.0 && temp == 0.0)) - continue; - - price_t k = (closePrice + temp) / (denominator * closePrice); - - for (i = 0; i < pre_pos; ++i) { - m_buffer[i].openPrice = roundEx(k * m_buffer[i].openPrice, m_stock.precision()); - m_buffer[i].highPrice = roundEx(k * m_buffer[i].highPrice, m_stock.precision()); - m_buffer[i].lowPrice = roundEx(k * m_buffer[i].lowPrice, m_stock.precision()); - m_buffer[i].closePrice = roundEx(k * m_buffer[i].closePrice, m_stock.precision()); - } - } -} - -/****************************************************************************** - * 等比后复权公式:复权后价格=复权前价格÷复权率 - * 复权率={[(股权登记日收盘价-现金红利)+配(新)股价格×流通股份变动比例]÷(1+流通股份变动比例)}÷股权登记日收盘价 - * 向后复权指以除权前的股价为基准(即除权前的股价不变),将除权后的股价升上去。复权计算时首先从最新日开始, - * 逐日向前判断,遇到除权日,则将除权日到最新日之间(包括除权日)的全部股价通过复权计算升上去;然后再继续 - * 向前判断,遇到下一个除权日,则再次将除权日到最新日之间(包括除权日)的全部股价通过复权计算升上去。 - *****************************************************************************/ -void KData::_recoverEqualBackward() { - size_t total = m_buffer.size(); - HKU_IF_RETURN(total == 0, void()); - - Datetime start_date(m_buffer.front().datetime.date()); - Datetime end_date(m_buffer.back().datetime.date() + bd::days(1)); - StockWeightList weightList = m_stock.getWeight(start_date, end_date); - StockWeightList::const_reverse_iterator weightIter = weightList.rbegin(); - StockWeightList::const_reverse_iterator pre_weightIter; - - size_t pre_pos = total - 1; - for (; weightIter != weightList.rend(); ++weightIter) { - size_t i = pre_pos; - while (i > 0 && m_buffer[i].datetime > weightIter->datetime()) { - i--; - } - pre_pos = i; //除权日 - - //股权登记日(即除权日的前一天数据)收盘价 - if (pre_pos == 0) { - continue; - } - price_t closePrice = m_buffer[pre_pos - 1].closePrice; - - //流通股份变动比例 - price_t change = 0.1 * (weightIter->countAsGift() + weightIter->countForSell() + - weightIter->increasement()); - price_t denominator = 1.0 + change; //(1+流通股份变动比例) - price_t temp = closePrice + weightIter->priceForSell() * change - 0.1 * weightIter->bonus(); - if (temp == 0.0 || denominator == 0.0) { - continue; - } - price_t k = (denominator * closePrice) / temp; - - for (i = pre_pos; i < total; ++i) { - m_buffer[i].openPrice = roundEx(k * m_buffer[i].openPrice, m_stock.precision()); - m_buffer[i].highPrice = roundEx(k * m_buffer[i].highPrice, m_stock.precision()); - m_buffer[i].lowPrice = roundEx(k * m_buffer[i].lowPrice, m_stock.precision()); - m_buffer[i].closePrice = roundEx(k * m_buffer[i].closePrice, m_stock.precision()); - } - } -} - void KData::tocsv(const string& filename) { std::ofstream file(filename.c_str()); HKU_ERROR_IF_RETURN(!file, void(), "Can't open file! ({})", filename); diff --git a/hikyuu_cpp/hikyuu/KData.h b/hikyuu_cpp/hikyuu/KData.h index 5e1f226a..98c49ce7 100644 --- a/hikyuu_cpp/hikyuu/KData.h +++ b/hikyuu_cpp/hikyuu/KData.h @@ -9,7 +9,7 @@ #ifndef KDATA_H_ #define KDATA_H_ -#include "Stock.h" +#include "KDataImp.h" namespace hku { @@ -45,7 +45,7 @@ public: /** 同getKRecord @see getKRecord */ KRecord operator[](size_t pos) const { - return m_buffer[pos]; + return getKRecord(pos); } /** 同getKRecord @see getKRecord */ @@ -59,12 +59,9 @@ public: /** 按日期获取在原始 K 线记录中的位置 */ size_t getPosInStock(Datetime datetime) const; - /** 获取关联的KQuery, 该 Query 已被同一调整为按索引方式的查询条件 */ + /** 获取关联的KQuery */ KQuery getQuery() const; - /** 获取原始查询条件 */ - KQuery getRawQUery() const; - /** 获取关联的Stock,如果没有关联返回Null */ Stock getStock() const; @@ -77,13 +74,6 @@ public: /** 获取在原始K线记录中对应范围的下一条记录的位置,如果为空返回0,其他等于lastPos + 1 */ size_t endPos() const; - /** - * 从当前结尾向后尝试从 Stock 扩展获取指定数量的 KRecord - * @param num 指定数量 - * @return size_t 实际被扩展的记录数 - */ - size_t expand(size_t num); - /** 输出数据到指定的文件中 */ void tocsv(const string& filename); @@ -108,19 +98,7 @@ public: Indicator amo() const; private: - void _recoverForward(); - void _recoverBackward(); - void _recoverEqualForward(); - void _recoverEqualBackward(); - void _recoverForUpDay(); - -private: - KRecordList m_buffer; - KQuery m_query; - KQuery m_raw_query; - Stock m_stock; - size_t m_start; - size_t m_end; + KDataImpPtr m_imp; }; /** @@ -171,8 +149,35 @@ KData HKU_API getKData(const string& market_code, int64_t start = 0, int64_t end KQuery::KType ktype = KQuery::DAY, KQuery::RecoverType recoverType = KQuery::NO_RECOVER); +inline KData::KData(const KData& x) : m_imp(x.m_imp) {} + +inline KData::KData(KData&& x) : m_imp(std::move(x.m_imp)) {} + +inline KData& KData::operator=(const KData& x) { + if (this == &x) + return *this; + m_imp = x.m_imp; + return *this; +} + +inline KData& KData::operator=(KData&& x) { + if (this == &x) + return *this; + m_imp = std::move(x.m_imp); + return *this; +} + +inline DatetimeList KData::getDatetimeList() const { + DatetimeList result; + if (empty()) { + return result; + } + result = getStock().getDatetimeList(KQuery(startPos(), lastPos() + 1, getQuery().kType())); + return result; +} + inline KRecord KData::getKRecord(size_t pos) const { - return m_buffer[pos]; + return m_imp->getKRecord(pos); //如果为空,将抛出异常 } inline KRecord KData::getKRecord(Datetime datetime) const { @@ -180,36 +185,36 @@ inline KRecord KData::getKRecord(Datetime datetime) const { return pos != Null() ? getKRecord(pos) : Null(); } +inline size_t KData::getPos(const Datetime& datetime) const { + return m_imp ? m_imp->getPos(datetime) : Null(); +} + inline size_t KData::size() const { - return m_buffer.size(); + return m_imp ? m_imp->size() : 0; } inline bool KData::empty() const { - return m_buffer.empty(); + return m_imp ? m_imp->empty() : true; } inline KQuery KData::getQuery() const { - return m_query; -} - -inline KQuery KData::getRawQUery() const { - return m_raw_query; + return m_imp ? m_imp->getQuery() : Null(); } inline Stock KData::getStock() const { - return m_stock; + return m_imp ? m_imp->getStock() : Null(); } inline size_t KData::startPos() const { - return m_start; + return m_imp ? m_imp->startPos() : 0; } inline size_t KData::endPos() const { - return m_end; + return m_imp ? m_imp->endPos() : 0; } inline size_t KData::lastPos() const { - return m_end == 0 ? 0 : m_end - 1; + return m_imp ? m_imp->lastPos() : 0; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/KDataImp.cpp b/hikyuu_cpp/hikyuu/KDataImp.cpp new file mode 100644 index 00000000..a6f26730 --- /dev/null +++ b/hikyuu_cpp/hikyuu/KDataImp.cpp @@ -0,0 +1,387 @@ +/* + * KDataImp.cpp + * + * Created on: 2013-2-4 + * Author: fasiondog + */ + +#include +#include +#include +#include "StockManager.h" +#include "KDataImp.h" + +namespace hku { + +KDataImp::KDataImp() : m_start(0), m_end(0), m_have_pos_in_stock(false) {} + +KDataImp::KDataImp(const Stock& stock, const KQuery& query) +: m_query(query), m_stock(stock), m_start(0), m_end(0), m_have_pos_in_stock(false) { + if (m_stock.isNull()) { + return; + } + + m_buffer = m_stock.getKRecordList(query); + + //不支持复权时,直接返回 + if (query.recoverType() == KQuery::NO_RECOVER) + return; + + //日线以上复权处理 + if (query.kType() == KQuery::WEEK || query.kType() == KQuery::MONTH || + query.kType() == KQuery::QUARTER || query.kType() == KQuery::HALFYEAR || + query.kType() == KQuery::YEAR) { + _recoverForUpDay(); + return; + } + + switch (query.recoverType()) { + case KQuery::NO_RECOVER: + // do nothing + break; + + case KQuery::FORWARD: + _recoverForward(); + break; + + case KQuery::BACKWARD: + _recoverBackward(); + break; + + case KQuery::EQUAL_FORWARD: + _recoverEqualForward(); + break; + + case KQuery::EQUAL_BACKWARD: + _recoverEqualBackward(); + break; + + default: + HKU_ERROR("Invalid RecvoerType!"); + return; + } +} + +KDataImp::~KDataImp() {} + +size_t KDataImp::startPos() { + if (!m_have_pos_in_stock) { + _getPosInStock(); + } + return m_start; +} + +size_t KDataImp::endPos() { + if (!m_have_pos_in_stock) { + _getPosInStock(); + } + return m_end; +} + +size_t KDataImp::lastPos() { + if (!m_have_pos_in_stock) { + _getPosInStock(); + } + return m_end == 0 ? 0 : m_end - 1; +} + +void KDataImp::_getPosInStock() { + bool sucess = m_stock.getIndexRange(m_query, m_start, m_end); + if (!sucess) { + m_start = 0; + m_end = 0; + } + m_have_pos_in_stock = true; +} + +size_t KDataImp::getPos(const Datetime& datetime) { + KRecordList::const_iterator iter; + KRecord comp_record; + comp_record.datetime = datetime; + boost::function f = + boost::bind(&KRecord::datetime, _1) < boost::bind(&KRecord::datetime, _2); + + iter = lower_bound(m_buffer.begin(), m_buffer.end(), comp_record, f); + if (iter == m_buffer.end() || iter->datetime != datetime) { + return Null(); + } + + return (iter - m_buffer.begin()); +} + +void KDataImp::_recoverForUpDay() { + HKU_IF_RETURN(empty(), void()); + std::function startOfPhase; + if (m_query.kType() == KQuery::WEEK) { + startOfPhase = &Datetime::startOfWeek; + } else if (m_query.kType() == KQuery::MONTH) { + startOfPhase = &Datetime::startOfMonth; + } else if (m_query.kType() == KQuery::QUARTER) { + startOfPhase = &Datetime::startOfQuarter; + } else if (m_query.kType() == KQuery::HALFYEAR) { + startOfPhase = &Datetime::startOfHalfyear; + } else if (m_query.kType() == KQuery::YEAR) { + startOfPhase = &Datetime::startOfYear; + } + + Datetime startDate = startOfPhase(m_buffer.front().datetime); + Datetime endDate = m_buffer.back().datetime.nextDay(); + KQuery query = KQueryByDate(startDate, endDate, KQuery::DAY, m_query.recoverType()); + KData day_list = m_stock.getKData(query); + if (day_list.empty()) + return; + + size_t day_pos = 0; + size_t day_total = day_list.size(); + size_t length = size(); + for (size_t i = 0; i < length; i++) { + Datetime phase_start_date = startOfPhase(m_buffer[i].datetime); + Datetime phase_end_date = m_buffer[i].datetime; + if (day_pos >= day_total) + break; + + while (day_list[day_pos].datetime < phase_start_date) { + day_pos++; + } + KRecord record = day_list[day_pos]; + int pre_day_pos = day_pos; + while (day_pos < day_total && day_list[day_pos].datetime <= phase_end_date) { + if (day_list[day_pos].lowPrice < record.lowPrice) { + record.lowPrice = day_list[day_pos].lowPrice; + } else if (day_list[day_pos].highPrice > record.highPrice) { + record.highPrice = day_list[day_pos].highPrice; + } + record.closePrice = day_list[day_pos].closePrice; + day_pos++; + } + if (pre_day_pos != day_pos) { + m_buffer[i].openPrice = record.openPrice; + m_buffer[i].highPrice = record.highPrice; + m_buffer[i].lowPrice = record.lowPrice; + m_buffer[i].closePrice = record.closePrice; + } + } + + return; +} + +/****************************************************************************** + * 前复权公式:复权后价格=[(复权前价格-现金红利)+配(新)股价格×流通股份变动比例]÷(1+流通股份变动比例) + * 向前复权指以除权后的股价为基准(即除权后的股价不变),将除权前的股价降下来。 + * 复权计算时首先从上市日开始,逐日向后判断,遇到除权日,则将上市日到除权日之间(不包括除权日)的 + * 全部股价通过复权计算降下来;然后再继续向后判断,遇到下一个除权日,则再次将上市日到该除权日之间 + * (不包括除权日)的全部股价通过复权计算降下来。 + *****************************************************************************/ +void KDataImp::_recoverForward() { + size_t total = m_buffer.size(); + HKU_IF_RETURN(total == 0, void()); + + Datetime start_date(m_buffer.front().datetime.date()); + Datetime end_date(m_buffer.back().datetime.date() + bd::days(1)); + StockWeightList weightList = m_stock.getWeight(start_date, end_date); + StockWeightList::const_iterator weightIter = weightList.begin(); + StockWeightList::const_iterator pre_weightIter = weightIter; + + size_t pre_pos = 0; + for (; weightIter != weightList.end(); ++weightIter) { + //计算流通股份变动比例,但不处理仅仅只有流通股本改变的情况 + if ((weightIter->countAsGift() == 0.0 && weightIter->countForSell() == 0.0 && + weightIter->priceForSell() == 0.0 && weightIter->bonus() == 0.0 && + weightIter->increasement() == 0.0)) + continue; + + size_t i = pre_pos; + while (i < total && m_buffer[i].datetime < weightIter->datetime()) { + i++; + } + pre_pos = i; //除权日 + + price_t change = 0.1 * (weightIter->countAsGift() + weightIter->countForSell() + + weightIter->increasement()); + price_t denominator = 1.0 + change; //分母 = (1+流通股份变动比例) + price_t temp = weightIter->priceForSell() * change - 0.1 * weightIter->bonus(); + + if (denominator == 1.0 && temp == 0.0) + continue; + + for (i = 0; i < pre_pos; ++i) { + m_buffer[i].openPrice = + roundEx((m_buffer[i].openPrice + temp) / denominator, m_stock.precision()); + m_buffer[i].highPrice = + roundEx((m_buffer[i].highPrice + temp) / denominator, m_stock.precision()); + m_buffer[i].lowPrice = + roundEx((m_buffer[i].lowPrice + temp) / denominator, m_stock.precision()); + m_buffer[i].closePrice = + roundEx((m_buffer[i].closePrice + temp) / denominator, m_stock.precision()); + } + } +} + +/****************************************************************************** + * 后复权公式:复权后价格=复权前价格×(1+流通股份变动比例)-配(新)股价格×流通股份变动比例+现金红利 + * 向后复权指以除权前的股价为基准(即除权前的股价不变),将除权后的股价升上去。复权计算时首先从最新日开始, + * 逐日向前判断,遇到除权日,则将除权日到最新日之间(包括除权日)的全部股价通过复权计算升上去;然后再继续 + * 向前判断,遇到下一个除权日,则再次将除权日到最新日之间(包括除权日)的全部股价通过复权计算升上去。 + *****************************************************************************/ +void KDataImp::_recoverBackward() { + size_t total = m_buffer.size(); + HKU_IF_RETURN(total == 0, void()); + + Datetime start_date(m_buffer.front().datetime.date()); + Datetime end_date(m_buffer.back().datetime.date() + bd::days(1)); + StockWeightList weightList = m_stock.getWeight(start_date, end_date); + StockWeightList::const_reverse_iterator weightIter = weightList.rbegin(); + StockWeightList::const_reverse_iterator pre_weightIter; + + size_t pre_pos = total - 1; + for (; weightIter != weightList.rend(); ++weightIter) { + //计算流通股份变动比例,但不处理仅仅只有流通股本改变的情况 + if ((weightIter->countAsGift() == 0.0 && weightIter->countForSell() == 0.0 && + weightIter->priceForSell() == 0.0 && weightIter->bonus() == 0.0 && + weightIter->increasement() == 0.0)) + continue; + + size_t i = pre_pos; + while (i > 0 && m_buffer[i].datetime > weightIter->datetime()) { + i--; + } + pre_pos = i; + + //流通股份变动比例 + price_t change = 0.1 * (weightIter->countAsGift() + weightIter->countForSell() + + weightIter->increasement()); + price_t denominator = 1.0 + change; //(1+流通股份变动比例) + price_t temp = 0.1 * weightIter->bonus() - weightIter->priceForSell() * change; + + if (denominator == 1.0 && temp == 0.0) + continue; + + for (i = pre_pos; i < total; ++i) { + m_buffer[i].openPrice = + roundEx(m_buffer[i].openPrice * denominator + temp, m_stock.precision()); + m_buffer[i].highPrice = + roundEx(m_buffer[i].highPrice * denominator + temp, m_stock.precision()); + m_buffer[i].lowPrice = + roundEx(m_buffer[i].lowPrice * denominator + temp, m_stock.precision()); + m_buffer[i].closePrice = + roundEx(m_buffer[i].closePrice * denominator + temp, m_stock.precision()); + } + } +} + +/****************************************************************************** + * 等比前复权公式:复权后价格=复权前价格*复权率 + * 复权率={[(股权登记日收盘价-现金红利)+配(新)股价格×流通股份变动比例]÷(1+流通股份变动比例)}÷股权登记日收盘价 + * 向前复权指以除权后的股价为基准(即除权后的股价不变),将除权前的股价降下来。 + * 复权计算时首先从上市日开始,逐日向后判断,遇到除权日,则将上市日到除权日之间(不包括除权日)的 + * 全部股价通过复权计算降下来;然后再继续向后判断,遇到下一个除权日,则再次将上市日到该除权日之间 + * (不包括除权日)的全部股价通过复权计算降下来。 + *****************************************************************************/ +void KDataImp::_recoverEqualForward() { + size_t total = m_buffer.size(); + HKU_IF_RETURN(total == 0, void()); + + Datetime start_date(m_buffer.front().datetime.date()); + Datetime end_date(m_buffer.back().datetime.date() + bd::days(1)); + StockWeightList weightList = m_stock.getWeight(start_date, end_date); + if (weightList.empty()) { + return; + } + + KRecordList kdata = m_buffer; //防止同一天两条权息记录 + StockWeightList::const_iterator weightIter = weightList.begin(); + StockWeightList::const_iterator pre_weightIter; + size_t pre_pos = 0; + for (; weightIter != weightList.end(); ++weightIter) { + //计算流通股份变动比例,但不处理仅仅只有流通股本改变的情况 + if ((weightIter->countAsGift() == 0.0 && weightIter->countForSell() == 0.0 && + weightIter->priceForSell() == 0.0 && weightIter->bonus() == 0.0 && + weightIter->increasement() == 0.0)) + continue; + + size_t i = pre_pos; + while (i < total && m_buffer[i].datetime < weightIter->datetime()) { + i++; + } + pre_pos = i; //除权日 + + //股权登记日(即除权日的前一天数据)收盘价 + if (pre_pos == 0) { + continue; + } + price_t closePrice = kdata[pre_pos - 1].closePrice; + if (closePrice == 0.0) { + continue; //除零保护 + } + + //流通股份变动比例 + price_t change = 0.1 * (weightIter->countAsGift() + weightIter->countForSell() + + weightIter->increasement()); + price_t denominator = 1.0 + change; //(1+流通股份变动比例) + price_t temp = weightIter->priceForSell() * change - 0.1 * weightIter->bonus(); + + if (denominator == 0.0 || (denominator == 1.0 && temp == 0.0)) + continue; + + price_t k = (closePrice + temp) / (denominator * closePrice); + + for (i = 0; i < pre_pos; ++i) { + m_buffer[i].openPrice = roundEx(k * m_buffer[i].openPrice, m_stock.precision()); + m_buffer[i].highPrice = roundEx(k * m_buffer[i].highPrice, m_stock.precision()); + m_buffer[i].lowPrice = roundEx(k * m_buffer[i].lowPrice, m_stock.precision()); + m_buffer[i].closePrice = roundEx(k * m_buffer[i].closePrice, m_stock.precision()); + } + } +} + +/****************************************************************************** + * 等比后复权公式:复权后价格=复权前价格÷复权率 + * 复权率={[(股权登记日收盘价-现金红利)+配(新)股价格×流通股份变动比例]÷(1+流通股份变动比例)}÷股权登记日收盘价 + * 向后复权指以除权前的股价为基准(即除权前的股价不变),将除权后的股价升上去。复权计算时首先从最新日开始, + * 逐日向前判断,遇到除权日,则将除权日到最新日之间(包括除权日)的全部股价通过复权计算升上去;然后再继续 + * 向前判断,遇到下一个除权日,则再次将除权日到最新日之间(包括除权日)的全部股价通过复权计算升上去。 + *****************************************************************************/ +void KDataImp::_recoverEqualBackward() { + size_t total = m_buffer.size(); + HKU_IF_RETURN(total == 0, void()); + + Datetime start_date(m_buffer.front().datetime.date()); + Datetime end_date(m_buffer.back().datetime.date() + bd::days(1)); + StockWeightList weightList = m_stock.getWeight(start_date, end_date); + StockWeightList::const_reverse_iterator weightIter = weightList.rbegin(); + StockWeightList::const_reverse_iterator pre_weightIter; + + size_t pre_pos = total - 1; + for (; weightIter != weightList.rend(); ++weightIter) { + size_t i = pre_pos; + while (i > 0 && m_buffer[i].datetime > weightIter->datetime()) { + i--; + } + pre_pos = i; //除权日 + + //股权登记日(即除权日的前一天数据)收盘价 + if (pre_pos == 0) { + continue; + } + price_t closePrice = m_buffer[pre_pos - 1].closePrice; + + //流通股份变动比例 + price_t change = 0.1 * (weightIter->countAsGift() + weightIter->countForSell() + + weightIter->increasement()); + price_t denominator = 1.0 + change; //(1+流通股份变动比例) + price_t temp = closePrice + weightIter->priceForSell() * change - 0.1 * weightIter->bonus(); + if (temp == 0.0 || denominator == 0.0) { + continue; + } + price_t k = (denominator * closePrice) / temp; + + for (i = pre_pos; i < total; ++i) { + m_buffer[i].openPrice = roundEx(k * m_buffer[i].openPrice, m_stock.precision()); + m_buffer[i].highPrice = roundEx(k * m_buffer[i].highPrice, m_stock.precision()); + m_buffer[i].lowPrice = roundEx(k * m_buffer[i].lowPrice, m_stock.precision()); + m_buffer[i].closePrice = roundEx(k * m_buffer[i].closePrice, m_stock.precision()); + } + } +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/KDataImp.h b/hikyuu_cpp/hikyuu/KDataImp.h new file mode 100644 index 00000000..40e1dd03 --- /dev/null +++ b/hikyuu_cpp/hikyuu/KDataImp.h @@ -0,0 +1,68 @@ +/* + * KDataImp.h + * + * Created on: 2013-2-4 + * Author: fasiondog + */ + +#pragma once +#ifndef KDATAIMP_H_ +#define KDATAIMP_H_ + +#include "Stock.h" + +namespace hku { + +class KDataImp { +public: + KDataImp(); + KDataImp(const Stock& stock, const KQuery& query); + virtual ~KDataImp(); + + KQuery getQuery() const { + return m_query; + } + + Stock getStock() const { + return m_stock; + } + + KRecord getKRecord(size_t pos) const { + return m_buffer[pos]; + } + + bool empty() const { + return m_buffer.empty(); + } + + size_t size() { + return m_buffer.size(); + } + + size_t startPos(); + size_t endPos(); + size_t lastPos(); + + size_t getPos(const Datetime& datetime); + +private: + void _getPosInStock(); + void _recoverForward(); + void _recoverBackward(); + void _recoverEqualForward(); + void _recoverEqualBackward(); + void _recoverForUpDay(); + +private: + KRecordList m_buffer; + KQuery m_query; + Stock m_stock; + size_t m_start; + size_t m_end; + bool m_have_pos_in_stock; +}; + +typedef shared_ptr KDataImpPtr; + +} /* namespace hku */ +#endif /* KDATAIMP_H_ */