CodeHelper.hpp
source: wtcpp/folder98/folder02/folder1/file01.md
/*!
* \file CodeHelper.hpp
* \project WonderTrader
*
* \author Wesley
* \date 2020/03/30
*
* \brief 代码辅助类,封装到一起方便使用
*/
#pragma once
#include "fmtlib.h"
#include "StrUtil.hpp"
#include "../Includes/WTSTypes.h"
#include <boost/xpressive/xpressive_dynamic.hpp>
/*
标准的股票代码: SSE.STK.600000Q
标准期货主力合约代码: CFFEX.IF.HOT
标准期货次主力合约代码: CFFEX.IF.2ND
期货期权合约代码: CFFEX.IO2007.C.4000
标准期货合约代码: CFFEX.IF.2007
标准品种ID: SHFE.ag
基础合约代码 ag1912
基础品种代码 ag
**交易所原生code, 格式如下:**
中金所, 大商所格式IO2013-C-4000
郑商所上期所期权代码格式ZC2010P11600
*/
USING_NS_WTP;
//主力合约后缀
static const char* SUFFIX_HOT = ".HOT";
static const char* FILE_SUF_HOT = "_HOT";
//次主力合约后缀
static const char* SUFFIX_2ND = ".2ND";
static const char* FILE_SUF_2ND = "_2ND";
//前复权合约代码后缀
static const char SUFFIX_QFQ = '-';
//后复权合约代码后缀
static const char SUFFIX_HFQ = '+';
class CodeHelper
{
public:
// 合约信息
typedef struct _CodeInfo
{
char _code[MAX_INSTRUMENT_LENGTH]; //合约代码
char _exchg[MAX_INSTRUMENT_LENGTH]; //交易所代码
char _product[MAX_INSTRUMENT_LENGTH]; //品种代码
union
{
uint8_t _hotflag; //主力标记,0-非主力,1-主力,2-次主力
uint8_t _exright; //是否是复权代码,如SH600000Q: 0-不复权, 1-前复权, 2-后复权
};
// 属性判断
inline bool isExright() const { return _exright != 0; }
inline bool isHot() const { return _hotflag ==1; }
inline bool isSecond() const { return _hotflag == 2; }
inline bool isFlat() const { return _hotflag == 0; }
inline const char* stdCommID() const
{
static char buffer[64] = { 0 };
if (strlen(buffer) == 0)
sprintf(buffer, "%s.%s", _exchg, _product);
return buffer;
}
// 结构体构造函数
_CodeInfo()
{
memset(this, 0, sizeof(_CodeInfo));
}
} CodeInfo;
private:
// 从src中找到symbol的索引
static inline std::size_t find(const char* src, char symbol = '.', bool bReverse = false)
{
std::size_t len = strlen(src);
if (len != 0)
{
if (bReverse)
{
for (std::size_t idx = len - 1; idx >= 0; idx--)
{
if (src[idx] == symbol)
return idx;
}
}
else
{
for (std::size_t idx = 0; idx < len; idx++)
{
if (src[idx] == symbol)
return idx;
}
}
}
return std::string::npos;
}
public:
/*
* 是否是标准期货主力合约代码
*/
static inline bool isStdFutHotCode(const char* stdCode)
{
return StrUtil::endsWith(stdCode, SUFFIX_HOT, false);
}
/*
* 是否是标准期货次主力合约代码
*/
static inline bool isStdFut2ndCode(const char* stdCode)
{
return StrUtil::endsWith(stdCode, SUFFIX_2ND, false);
}
/*
* 是否是期货期权合约代码
* CFFEX.IO2007.C.4000
*/
static inline bool isStdChnFutOptCode(const char* code)
{
using namespace boost::xpressive;
/* 定义正则表达式 */
static cregex reg_stk = cregex::compile("^[A-Z]+.[A-z]+\\d{4}.(C|P).\\d+$"); //CFFEX.IO2007.C.4000
return regex_match(code, reg_stk);
}
/*
* 是否是标准分月期货合约代码
* //CFFEX.IF.2007
*/
static inline bool isStdMonthlyFutCode(const char* code)
{
using namespace boost::xpressive;
/* 定义正则表达式 */
static cregex reg_stk = cregex::compile("^[A-Z]+.[A-z]+.\\d{4}$"); //CFFEX.IO.2007
return regex_match(code, reg_stk);
}
/*
* 标准代码转标准品种ID
* 如SHFE.ag.1912->SHFE.ag
* 如果是简化的股票代码,如SSE.600000,则转成SSE.STK
*/
static inline std::string stdCodeToStdCommID(const char* stdCode)
{
auto idx = find(stdCode, '.', true);
auto idx2 = find(stdCode, '.', false);
if(idx != idx2)
{
//前后两个.不是同一个,说明是三段的代码
//提取前两段作为品种代码
std::string stdCommID(stdCode, idx);
return std::move(stdCommID);
}
else
{
//两段的代码,直接返回
//主要针对某些交易所,每个合约的交易规则都不同的情况
//这种情况,就把合约直接当成品种来用
return std::move(stdCode);
}
}
/*
* 从基础分月合约代码提取基础品种代码
* 如ag1912 -> ag
* 这个只有分月期货品种才有意义
* 这个不会有永续合约的代码传到这里来,如果有的话就是调用的地方有Bug!
*/
static inline std::string rawMonthCodeToRawCommID(const char* code)
{
int nLen = 0;
while ('A' <= code[nLen] && code[nLen] <= 'z')
nLen++;
std::string strRet(code, nLen);
return std::move(strRet);
}
/*
* 基础分月合约代码转标准码
* 如ag1912转成全码
* 这个不会有永续合约的代码传到这里来,如果有的话就是调用的地方有Bug!
*/
static inline std::string rawMonthCodeToStdCode(const char* code, const char* exchg, bool isComm = false)
{
std::string pid = code;
if (!isComm)
pid = rawMonthCodeToRawCommID(code);
std::string ret = fmt::format("{}.{}", exchg, pid);
if (!isComm)
{
ret += ".";
char* s = (char*)code;
s += pid.size();
if(strlen(s) == 4)
{
ret += s;
}
else
{
if (s[0] == '9')
ret += "1";
else
ret += "2";
ret += s;
}
}
return std::move(ret);
}
/*
* 原始常规代码转标准代码
* 这种主要针对非分月合约而言
*/
static inline std::string rawFlatCodeToStdCode(const char* code, const char* exchg, const char* pid)
{
if(strcmp(code, pid) == 0 || strlen(pid) == 0)
return std::move(fmt::format("{}.{}", exchg, pid));
else
return std::move(fmt::format("{}.{}.{}", exchg, pid, code));
}
static inline bool isMonthlyCode(const char* code)
{
using namespace boost::xpressive;
//最后3-6位都是数字,才是分月合约
static cregex reg_stk = cregex::compile("^.*[A-z|-]\\d{3,6}$"); //CFFEX.IO.2007
return regex_match(code, reg_stk);
}
/*
* 期货期权代码标准化
* 标准期货期权代码格式为CFFEX.IO2008.C.4300
* -- 暂时没有地方调用 --
*/
static inline std::string rawFutOptCodeToStdCode(const char* code, const char* exchg)
{
using namespace boost::xpressive;
/* 定义正则表达式 */
static cregex reg_stk = cregex::compile("^[A-z]+\\d{4}-(C|P)-\\d+$"); //中金所、大商所格式IO2013-C-4000
bool bMatch = regex_match(code, reg_stk);
if(bMatch)
{
std::string s = std::move(fmt::format("{}.{}", exchg, code));
StrUtil::replace(s, "-", ".");
return std::move(s);
}
else
{
//郑商所上期所期权代码格式ZC2010P11600
//先从后往前定位到P或C的位置
std::size_t idx = strlen(code) - 1;
for(; idx >= 0; idx--)
{
if(!isdigit(code[idx]))
break;
}
std::string s = exchg;
s.append(".");
s.append(code, idx);
s.append(".");
s.append(&code[idx], 1);
s.append(".");
s.append(&code[idx + 1]);
return std::move(s);
}
}
/*
* 标准合约代码转主力代码
*/
static inline std::string stdCodeToStdHotCode(const char* stdCode)
{
std::size_t idx = find(stdCode, '.', true);
if (idx == std::string::npos)
return "";
std::string stdWrappedCode;
stdWrappedCode.resize(idx + strlen(SUFFIX_HOT) + 1);
strncpy((char*)stdWrappedCode.data(), stdCode, idx);
strcpy((char*)stdWrappedCode.data()+idx, SUFFIX_HOT);
return std::move(stdWrappedCode);
}
/*
* 标准合约代码转次主力代码
*/
static inline std::string stdCodeToStd2ndCode(const char* stdCode)
{
std::size_t idx = find(stdCode, '.', true);
if (idx == std::string::npos)
return "";
std::string stdWrappedCode;
stdWrappedCode.resize(idx + strlen(SUFFIX_2ND) + 1);
strncpy((char*)stdWrappedCode.data(), stdCode, idx);
strcpy((char*)stdWrappedCode.data() + idx, SUFFIX_2ND);
return std::move(stdWrappedCode);
}
/*
* 标准期货期权代码转原代码
* -- 暂时没有地方调用 --
*/
static inline std::string stdFutOptCodeToRawCode(const char* stdCode)
{
std::string ret = stdCode;
auto pos = ret.find(".");
ret = ret.substr(pos + 1);
if (strncmp(stdCode, "CFFEX", 5) == 0 || strncmp(stdCode, "DCE", 3) == 0)
StrUtil::replace(ret, ".", "-");
else
StrUtil::replace(ret, ".", "");
return std::move(ret);
}
static inline int indexCodeMonth(const char* code)
{
if (strlen(code) == 0)
return -1;
std::size_t idx = 0;
std::size_t len = strlen(code);
while(idx < len)
{
if (isdigit(code[idx]))
return (int)idx;
idx++;
}
return -1;
}
/*
* 提取标准期货期权代码的信息
*/
static inline CodeInfo extractStdChnFutOptCode(const char* stdCode)
{
CodeInfo codeInfo;
StringVector ay = StrUtil::split(stdCode, ".");
strcpy(codeInfo._exchg, ay[0].c_str());
if(strcmp(codeInfo._exchg, "SHFE") == 0 || strcmp(codeInfo._exchg, "CZCE") == 0)
{
fmt::format_to(codeInfo._code, "{}{}{}", ay[1], ay[2], ay[3]);
}
else
{
fmt::format_to(codeInfo._code, "{}-{}-{}", ay[1], ay[2], ay[3]);
}
int mpos = indexCodeMonth(ay[1].c_str());
if(strcmp(codeInfo._exchg, "CZCE") == 0)
{
strncpy(codeInfo._product, ay[1].c_str(), mpos);
strcat(codeInfo._product, ay[2].c_str());
}
else if (strcmp(codeInfo._exchg, "CFFEX") == 0)
{
strncpy(codeInfo._product, ay[1].c_str(), mpos);
}
else
{
strncpy(codeInfo._product, ay[1].c_str(), mpos);
strcat(codeInfo._product, "_o");
}
return std::move(codeInfo);
}
/*
* 提起标准代码的信息
*/
static CodeInfo extractStdCode(const char* stdCode)
{
//期权的代码规则和其他都不一样,所以单独判断
if(isStdChnFutOptCode(stdCode))
{
return std::move(extractStdChnFutOptCode(stdCode));
}
else
{
/*
* By Wesley @ 2021.12.25
* 1、先看是不是Q和H结尾的,如果是复权标记确认以后,最后一段长度-1,复制到code,如SSE.STK.600000Q
* 2、再看是不是分月合约,如果是,则将product字段拼接月份给code(郑商所特殊处理),如CFFEX.IF.2112
* 3、最后看看是不是HOT和2ND结尾的,如果是,则将product拷贝给code,如DCE.m.HOT
* 4、如果都不是,则原样复制第三段,如BINANCE.DC.BTCUSDT/SSE.STK.600000
*/
CodeInfo codeInfo;
StringVector ay = StrUtil::split(stdCode, ".");
strcpy(codeInfo._exchg, ay[0].c_str());
strcpy(codeInfo._product, ay[1].c_str());
if(ay.size() == 3)
{
if (ay[2].back() == SUFFIX_QFQ || ay[2].back() == SUFFIX_HFQ)
{
strcpy(codeInfo._code, ay[2].substr(0, ay[2].size() - 1).c_str());
codeInfo._exright = (ay[2].back() == SUFFIX_QFQ) ? 1 : 2;
}
else if (ay[2].size() == 4 && isdigit(ay[2].back()))
{
//如果最后一段是4位数字,说明是分月合约
//TODO: 这样的判断存在一个假设,最后一位是数字的一定是期货分月合约,以后可能会有问题,先注释一下
//那么code得加上品种id
//郑商所得单独处理一下,这个只能hardcode了
if (strcmp(codeInfo._product, "CZCE") == 0)
fmt::format_to(codeInfo._code, "{}{}", codeInfo._product, ay[2].c_str() + 1);
else
fmt::format_to(codeInfo._code, "{}{}", codeInfo._product, ay[2]);
}
else
{
codeInfo._hotflag = CodeHelper::isStdFutHotCode(stdCode) ? 1 : (CodeHelper::isStdFut2ndCode(stdCode) ? 2 : 0);
if (codeInfo._hotflag == 0)
strcpy(codeInfo._code, ay[2].c_str());
else
strcpy(codeInfo._code, codeInfo._product);
}
}
else
{
//By Wesley @ 2021.12.29
//如果是两段的合约代码,如OKEX.BTC-USDT
//则品种代码和合约代码一致
strcpy(codeInfo._code, ay[1].c_str());
}
return codeInfo;
}
}
};