#include "pch.h"
#include "TCalcFuncSets.h"
#include <windows.h>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <map>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <cctype>
#include <unordered_map>
#include <mutex>
#include <memory> // 用于std::make_shared
#include <chrono> // 用于std::chrono时间功能
// 缓存项结构体
struct CacheEntry {
std::map<std::string, float> data;
std::chrono::steady_clock::time_point lastAccessed;
CacheEntry() : lastAccessed(std::chrono::steady_clock::now()) {}
};
// 统一的缓存管理类
class QuantileCache {
private:
std::unordered_map<std::string, CacheEntry> cache_;
std::mutex mutex_;
const size_t maxCacheSize_ = 100; // 最大缓存项数量
public:
// 尝试从缓存获取数据
bool tryGet(const std::string& key, std::map<std::string, float>& result) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = cache_.find(key);
if (it != cache_.end()) {
// 更新访问时间
it->second.lastAccessed = std::chrono::steady_clock::now();
result = it->second.data;
return true;
}
return false;
}
// 存储数据到缓存
void put(const std::string& key, const std::map<std::string, float>& data) {
std::lock_guard<std::mutex> lock(mutex_);
// 清理过期缓存项
if (cache_.size() >= maxCacheSize_) {
evictOldEntries();
}
cache_[key].data = data;
cache_[key].lastAccessed = std::chrono::steady_clock::now();
}
private:
// 移除最旧的缓存项
void evictOldEntries() {
// 找到最旧的缓存项
auto oldestIt = cache_.begin();
for (auto it = cache_.begin(); it != cache_.end(); ++it) {
if (it->second.lastAccessed < oldestIt->second.lastAccessed) {
oldestIt = it;
}
}
// 移除最旧的缓存项
cache_.erase(oldestIt);
}
};
// 全局缓存实例
static QuantileCache g_quantileCache;
// 清理字符串中的空白字符
std::string CleanString(const std::string& str) {
std::string result;
result.reserve(str.size());
for (unsigned int i = 0; i < str.size(); i++) {
char c = str[i];
if (!std::isspace((unsigned char)c)) {
result.push_back(c);
}
}
return result;
}
// 清理路径中的无效字符
std::string CleanPath(const std::string& path) {
std::string result;
result.reserve(path.size());
for (unsigned int i = 0; i < path.size(); i++) {
char c = path[i];
if (c == ' ' || !std::isspace((unsigned char)c)) {
result.push_back(c);
}
}
return result;
}
// 将通达信日期格式转换为标准日期格式(yyyymmdd)
std::string ConvertTDXDateToYYYYMMDD(float tdxDate) {
int dateInt = (int)tdxDate + 19000000;
if (dateInt <= 19000000 || dateInt > 21000000) {
return "";
}
char buffer[16];
sprintf_s(buffer, sizeof(buffer), "%d", dateInt);
return std::string(buffer);
}
// 快速解析日期字符串
std::string ParseDateString(const std::string& dateStr) {
// 移除引号
std::string cleaned = dateStr;
if (!cleaned.empty() && cleaned[0] == '"') {
cleaned = cleaned.substr(1);
}
if (!cleaned.empty() && cleaned[cleaned.length() - 1] == '"') {
cleaned = cleaned.substr(0, cleaned.length() - 1);
}
// 如果已经是yyyymmdd格式
if (cleaned.length() == 8 && cleaned.find_first_not_of("0123456789") == std::string::npos) {
return cleaned;
}
// 处理分隔符格式
int year = 0, month = 0, day = 0;
size_t firstDelim = cleaned.find_first_of("-/\/");
if (firstDelim != std::string::npos) {
size_t secondDelim = cleaned.find_first_of("-/\/", firstDelim + 1);
if (secondDelim != std::string::npos) {
year = atoi(cleaned.substr(0, firstDelim).c_str());
month = atoi(cleaned.substr(firstDelim + 1, secondDelim - firstDelim - 1).c_str());
day = atoi(cleaned.substr(secondDelim + 1).c_str());
}
}
else if (cleaned.length() == 8) {
year = atoi(cleaned.substr(0, 4).c_str());
month = atoi(cleaned.substr(4, 2).c_str());
day = atoi(cleaned.substr(6, 2).c_str());
}
if (year > 1900 && year < 2100 && month >= 1 && month <= 12 && day >= 1 && day <= 31) {
char buffer[16];
sprintf_s(buffer, sizeof(buffer), "%04d%02d%02d", year, month, day);
return std::string(buffer);
}
return "";
}
// 处理单个CSV文件并提取指定列的数据
bool ProcessSingleCSVFileForColumn(const std::string& filePath,
std::map<std::string, std::vector<double> >& dateColumnMap,
int columnIdx) {
std::ifstream inFile(filePath.c_str());
if (!inFile.is_open()) {
return false;
}
// 跳过表头
std::string headerLine;
if (!std::getline(inFile, headerLine)) {
inFile.close();
return false;
}
std::string line;
while (std::getline(inFile, line)) {
std::istringstream lineIss(line);
std::string token;
int columnIndex = 0;
std::string dateStr;
double columnValue = 0.0;
bool hasValue = false;
while (std::getline(lineIss, token, ',')) {
std::string cleanedToken = CleanString(token);
if (columnIndex == 0) { // date列
dateStr = ParseDateString(cleanedToken);
}
else if (columnIndex == columnIdx) { // 指定列
if (!cleanedToken.empty()) {
columnValue = atof(cleanedToken.c_str());
hasValue = true;
}
}
columnIndex++;
}
// 只有当日期有效且指定列有值时才添加
if (!dateStr.empty() && hasValue) {
dateColumnMap[dateStr].push_back(columnValue);
}
}
inFile.close();
return true;
}
// 处理单个CSV文件并计算N日涨跌幅
bool ProcessSingleCSVFileForReturns(const std::string& filePath,
std::map<std::string, std::vector<double> >& dateReturnsMap,
int N) {
std::ifstream inFile(filePath.c_str());
if (!inFile.is_open()) {
return false;
}
// 跳过表头
std::string headerLine;
if (!std::getline(inFile, headerLine)) {
inFile.close();
return false;
}
// 预分配向量空间以提高性能
std::vector<std::string> dates;
std::vector<double> closePrices;
dates.reserve(1000); // 预估每只股票约1000个交易日
closePrices.reserve(1000);
std::string line;
while (std::getline(inFile, line)) {
std::istringstream lineIss(line);
std::string token;
int columnIndex = 0;
std::string dateStr;
double closePrice = 0.0;
while (std::getline(lineIss, token, ',')) {
std::string cleanedToken = CleanString(token);
if (columnIndex == 0) { // date列
dateStr = ParseDateString(cleanedToken);
}
else if (columnIndex == 5) { // close列
if (!cleanedToken.empty()) {
closePrice = atof(cleanedToken.c_str());
}
}
columnIndex++;
}
dates.push_back(dateStr);
closePrices.push_back(closePrice);
}
inFile.close();
// 计算N日涨跌幅
for (unsigned int i = N; i < closePrices.size(); i++) {
if (!dates[i].empty() && closePrices[i] > 0 && closePrices[i - N] > 0) {
double returnRate = (closePrices[i] - closePrices[i - N]) / closePrices[i - N] * 100.0;
dateReturnsMap[dates[i]].push_back(returnRate);
}
}
return true;
}
// 优化的文件处理函数 - 用于计算指定列的分位数
void ReadCSVAndCalculateColumnData(const std::string& directory,
std::map<std::string, std::vector<double> >& dateColumnMap,
int columnIdx) {
WIN32_FIND_DATAA findData;
std::string cleanedDirectory = CleanPath(directory);
std::string searchPath = cleanedDirectory + "\\*.csv";
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE) {
return;
}
do {
if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
std::string fileName(findData.cFileName);
std::string filePath = cleanedDirectory + "\\" + fileName;
filePath = CleanPath(filePath);
ProcessSingleCSVFileForColumn(filePath, dateColumnMap, columnIdx);
}
} while (FindNextFileA(hFind, &findData) != 0);
FindClose(hFind);
}
// 优化的文件处理函数 - 用于计算N日涨跌幅
void ReadCSVAndCalculateReturns(const std::string& directory,
std::map<std::string, std::vector<double> >& dateReturnsMap,
int N) {
WIN32_FIND_DATAA findData;
std::string cleanedDirectory = CleanPath(directory);
std::string searchPath = cleanedDirectory + "\\*.csv";
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE) {
return;
}
do {
if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
std::string fileName(findData.cFileName);
std::string filePath = cleanedDirectory + "\\" + fileName;
filePath = CleanPath(filePath);
ProcessSingleCSVFileForReturns(filePath, dateReturnsMap, N);
}
} while (FindNextFileA(hFind, &findData) != 0);
FindClose(hFind);
}
// 函数前向声明
template <typename DataReaderFunc>
void ProcessQuantileData(
const std::string& csvDirectory,
std::map<std::string, float>& dateQuantileMap,
DataReaderFunc readDataFunc,
float quantile
);
template <typename DataReaderFunc>
void ProcessQuantileData(
const std::string& csvDirectory,
std::map<std::string, float>& dateQuantileMap,
DataReaderFunc readDataFunc,
float quantile,
std::map<std::string, std::vector<double>>& dateDataMap
);
float CalculateQuantile(const std::vector<double>& data, float quantile);
// 通用分位数数据处理函数
// 提取两个主要函数的公共数据处理逻辑
template <typename DataReaderFunc>
void ProcessQuantileData(
const std::string& csvDirectory,
std::map<std::string, float>& dateQuantileMap,
DataReaderFunc readDataFunc,
float quantile
) {
// 存储日期对应的数据
std::map<std::string, std::vector<double>> dateDataMap;
// 调用传入的函数读取数据
readDataFunc(csvDirectory, dateDataMap);
// 计算每个日期的分位数
for (const auto& entry : dateDataMap) {
dateQuantileMap[entry.first] = CalculateQuantile(entry.second, quantile);
}
// 清理数据以释放内存
dateDataMap.clear();
}
// 兼容不同参数类型的重载版本
template <typename DataReaderFunc>
void ProcessQuantileData(
const std::string& csvDirectory,
std::map<std::string, float>& dateQuantileMap,
DataReaderFunc readDataFunc,
float quantile,
std::map<std::string, std::vector<double>>& dateDataMap
) {
// 调用传入的函数读取数据
readDataFunc(csvDirectory, dateDataMap);
// 计算每个日期的分位数
for (const auto& entry : dateDataMap) {
dateQuantileMap[entry.first] = CalculateQuantile(entry.second, quantile);
}
}
// 计算分位数 - 使用O(n)时间复杂度的优化算法
float CalculateQuantile(const std::vector<double>& data, float quantile) {
if (data.empty()) {
return 0.0f;
}
if (quantile < 0.0f) quantile = 0.0f;
if (quantile > 1.0f) quantile = 1.0f;
std::vector<double> sortedData = data;
size_t n = sortedData.size();
double pos = quantile * (n - 1);
int lowerIndex = static_cast<int>(pos);
int upperIndex = lowerIndex + 1;
// 对于精确索引的情况,直接使用nth_element
if (lowerIndex == upperIndex || upperIndex >= static_cast<int>(n)) {
std::nth_element(sortedData.begin(), sortedData.begin() + lowerIndex, sortedData.end());
return static_cast<float>(sortedData[lowerIndex]);
}
// 对于需要插值的情况,找到两个位置的元素
// 先找lowerIndex位置的元素
std::nth_element(sortedData.begin(), sortedData.begin() + lowerIndex, sortedData.end());
double lowerValue = sortedData[lowerIndex];
// 再在lowerIndex之后的范围内找upperIndex位置的元素
std::nth_element(sortedData.begin() + lowerIndex + 1,
sortedData.begin() + upperIndex,
sortedData.end());
double upperValue = sortedData[upperIndex];
// 线性插值计算最终分位数值
double fractionalPart = pos - lowerIndex;
double quantileValue = lowerValue + fractionalPart * (upperValue - lowerValue);
return static_cast<float>(quantileValue);
}
// 生成缓存键值
std::string GenerateColumnCacheKey(int columnIdx, float quantile) {
char buffer[64];
sprintf_s(buffer, "column_%d_%.2f", columnIdx, quantile);
return std::string(buffer);
}
std::string GenerateReturnsCacheKey(int N, float quantile) {
char buffer[64];
sprintf_s(buffer, "returns_%d_%.2f", N, quantile);
return std::string(buffer);
}
// 函数2 - 计算指定列的分位数
void CalculateColumnQuantileReturns(int DataLen, float* pfOUT, float* pfINa, float* pfINb, float* pfINc)
{
// 初始化输出数组为0
for (int i = 0; i < DataLen; i++) {
pfOUT[i] = 0.0f;
}
// 获取列索引和分位数参数
int columnIdx = (int)(pfINb[0] + 0.5f); // M参数表示列号
float quantile = pfINc[0]; // N参数表示分位数
// 生成缓存键值,添加前缀以区分不同类型的缓存
std::string cacheKey = "column:" + GenerateColumnCacheKey(columnIdx, quantile);
// 检查缓存中是否已有计算结果
std::map<std::string, float> dateQuantileMap;
bool hasCache = g_quantileCache.tryGet(cacheKey, dateQuantileMap);
// 如果缓存中没有,则进行计算
if (!hasCache) {
// 设置CSV文件的目录
const std::string csvDirectory = "F:\\His_STOCKDATA";
// 使用堆分配存储日期对应的指定列数据,避免栈溢出
auto dateColumnMap = std::make_shared<std::map<std::string, std::vector<double>>>();
// 读取CSV文件并提取指定列的数据
ReadCSVAndCalculateColumnData(csvDirectory, *dateColumnMap, columnIdx);
// 计算每个日期的分位数
for (const auto& entry : *dateColumnMap) {
dateQuantileMap[entry.first] = CalculateQuantile(entry.second, quantile);
}
// 自动释放内存(通过shared_ptr的作用域)
// 将结果存入缓存
g_quantileCache.put(cacheKey, dateQuantileMap);
}
// 将结果匹配到输出数组
for (int i = 0; i < DataLen; i++) {
std::string dateStr = ConvertTDXDateToYYYYMMDD(pfINa[i]);
if (dateStr.empty() || dateStr.length() != 8) {
pfOUT[i] = 0.0f;
continue;
}
std::map<std::string, float>::const_iterator iter = dateQuantileMap.find(dateStr);
if (iter != dateQuantileMap.end()) {
pfOUT[i] = iter->second;
}
else {
pfOUT[i] = 0.0f;
}
}
}
// 函数3 - 快速计算分位数收益
void CalculateQuantileReturns(int DataLen, float* pfOUT, float* pfINa, float* pfINb, float* pfINc)
{
// 初始化输出数组为0
for (int i = 0; i < DataLen; i++) {
pfOUT[i] = 0.0f;
}
// 获取N和M的值
int N = (int)(pfINb[0] + 0.5f);
float M = pfINc[0];
// 生成缓存键值,添加前缀以区分不同类型的缓存
std::string cacheKey = "returns:" + GenerateReturnsCacheKey(N, M);
// 检查缓存中是否已有计算结果
std::map<std::string, float> dateQuantileMap;
bool hasCache = g_quantileCache.tryGet(cacheKey, dateQuantileMap);
// 如果缓存中没有,则进行计算
if (!hasCache) {
// 设置CSV文件的目录
const std::string csvDirectory = "F:\\His_STOCKDATA";
// 使用堆分配存储日期对应的涨跌幅数据,避免栈溢出
auto dateReturnsMap = std::make_shared<std::map<std::string, std::vector<double>>>();
// 读取CSV文件并计算N日涨跌幅
ReadCSVAndCalculateReturns(csvDirectory, *dateReturnsMap, N);
// 计算每个日期的分位数
for (const auto& entry : *dateReturnsMap) {
dateQuantileMap[entry.first] = CalculateQuantile(entry.second, M);
}
// 自动释放内存(通过shared_ptr的作用域)
// 将结果存入缓存
g_quantileCache.put(cacheKey, dateQuantileMap);
}
// 将结果匹配到输出数组
for (int i = 0; i < DataLen; i++) {
std::string dateStr = ConvertTDXDateToYYYYMMDD(pfINa[i]);
if (dateStr.empty() || dateStr.length() != 8) {
pfOUT[i] = 0.0f;
continue;
}
std::map<std::string, float>::const_iterator iter = dateQuantileMap.find(dateStr);
if (iter != dateQuantileMap.end()) {
pfOUT[i] = iter->second;
}
else {
pfOUT[i] = 0.0f;
}
}
}
// 函数注册
PluginTCalcFuncInfo g_CalcFuncSets[] = {
{2, (pPluginFUNC)CalculateColumnQuantileReturns},
{3, (pPluginFUNC)CalculateQuantileReturns},
{0, NULL},
};
BOOL RegisterTdxFunc(PluginTCalcFuncInfo** pFun)
{
if (*pFun == NULL) {
(*pFun) = g_CalcFuncSets;
return TRUE;
}
return FALSE;
}有哪些值得优化的地方