debug pf for not delay sys

This commit is contained in:
fasiondog 2024-04-01 00:31:27 +08:00
parent 4154e0a847
commit 975d764eab
6 changed files with 198 additions and 70 deletions

View File

@ -132,7 +132,7 @@ public:
/** 获取不同类型K线数据量 */
size_t getCount(KQuery::KType dataType = KQuery::DAY) const;
/** 获取指定日期时刻的市值,即小于等于指定日期的最后一条记录的收盘价 */
/** 获取指定日期时刻的市值,即小于等于指定日期的最后一条记录的收盘价, 如果证券已失效则为0 */
price_t getMarketValue(const Datetime&, KQuery::KType) const;
/**

View File

@ -266,7 +266,21 @@ SystemWeightList AllocateFundsBase::_adjust_with_running(
bool trace = getParam<bool>("trace");
HKU_INFO_IF(trace, "[AF] {} _adjust_with_running", date);
HKU_IF_RETURN(se_list.size() == 0, delay_list);
// 如果选中列表为空,则需要全部执行清仓,这里不能返回
// HKU_IF_RETURN(se_list.size() == 0, delay_list);
//-----------------------------------------------------------------
// 回收所有运行中系统剩余资金,用于重新分配
//-----------------------------------------------------------------
for (const auto& sys : running_set) {
auto sub_tm = sys->getTM();
auto sub_cash = sub_tm->currentCash();
if (sub_cash > 0.0 && sub_tm->checkout(date, sub_cash)) {
m_cash_tm->checkin(date, sub_cash);
HKU_INFO_IF(trace, "[AF] Recycle cash: {:<.2f} from {}", sub_cash, sys->name());
}
}
// 获取计划分配的资产权重
SystemWeightList sw_list = _allocateWeight(date, se_list);
@ -281,30 +295,40 @@ SystemWeightList AllocateFundsBase::_adjust_with_running(
//-----------------------------------------------------------------
// 先将已不在 sw_list 中的运行系统进行清仓,回收可分配资金
//-----------------------------------------------------------------
std::unordered_set<SYSPtr> running_in_sw_list;
std::unordered_set<SYSPtr> running_in_sw_set;
for (const auto& sw : sw_list) {
if (running_set.find(sw.sys) != running_set.cend()) {
running_in_sw_list.insert(sw.sys);
running_in_sw_set.insert(sw.sys);
}
}
for (const auto& sys : running_set) {
if (running_in_sw_list.find(sys) == running_in_sw_list.cend()) {
if (running_in_sw_set.find(sys) == running_in_sw_set.cend()) {
PositionRecord position = sys->getTM()->getPosition(date, sys->getStock());
if (sys->getParam<bool>("buy_delay")) {
delay_list.emplace_back(sys, MAX_DOUBLE);
// 延迟买入的系统,上一调仓日指示买入,可能尚未被执行,需要放入延迟列表中
delay_list.emplace_back(sys, position.number);
HKU_INFO_IF(trace, "[AF] Clean delay {}", sys->name());
} else {
// 非延迟卖出的系统,立即强制卖出并回收资金
auto tr = sys->sellForceOnClose(date, MAX_DOUBLE, PART_ALLOCATEFUNDS);
// HKU_DEBUG_IF(trace && tr.isNull(), "[AF] failed to sell: {}", sys->name());
auto tr = sys->sellForceOnClose(date, position.number, PART_ALLOCATEFUNDS);
if (!tr.isNull()) {
auto sub_tm = sys->getTM();
auto sub_cash = sub_tm->currentCash();
if (sub_tm->checkout(date, sub_cash)) {
m_cash_tm->checkin(date, sub_cash);
m_tm->addTradeRecord(tr); // 向总账户加入交易记录
HKU_INFO_IF(trace, "[AF] Adjust sell: {}, recycle cash: {:<.2f}",
HKU_INFO_IF(trace, "[AF] Clean position sell: {}, recycle cash: {:<.2f}",
sys->name(), sub_cash);
}
} else {
// 清仓卖出失败情况,也加入到延迟卖出列表中,以便下一交易日可执行
PositionRecord position = sys->getTM()->getPosition(date, sys->getStock());
if (position.number > 0.0) {
delay_list.emplace_back(sys, position.number);
HKU_INFO_IF(trace, "[AF] Clean delay {}", sys->name());
}
}
}
}
@ -316,8 +340,7 @@ SystemWeightList AllocateFundsBase::_adjust_with_running(
// 获取当前总资产市值,计算需保留的资产
int precision = m_cash_tm->getParam<int>("precision");
FundsRecord funds = m_tm->getFunds(date, m_query.kType());
price_t total_funds =
funds.cash + funds.market_value + funds.borrow_asset - funds.short_market_value;
price_t total_funds = funds.total_assets();
price_t reserve_funds = roundEx(total_funds * reserve_percent, precision);
std::unordered_set<SYSPtr> reduced_running_set; // 缓存已执行过减仓的运行中系统
@ -327,34 +350,62 @@ SystemWeightList AllocateFundsBase::_adjust_with_running(
TMPtr sub_tm = iter->sys->getTM();
const KQuery& query = iter->sys->getTO().getQuery();
FundsRecord sub_funds = sub_tm->getFunds(date, query.kType());
price_t sub_total_funds = sub_funds.cash + sub_funds.market_value +
sub_funds.borrow_asset - sub_funds.short_market_value;
price_t sub_total_funds = sub_funds.total_assets();
price_t sub_will_funds = total_funds * iter->weight;
// 如果需要执行减仓
if (sub_total_funds > sub_will_funds) {
reduced_running_set.insert(iter->sys); // 缓存执行了减仓的系统
price_t need_back_funds = sub_total_funds - sub_will_funds;
Stock stock = iter->sys->getStock();
KRecord krecord = stock.getKRecord(date, query.kType());
size_t need_back_shou =
size_t(need_back_funds / krecord.closePrice / stock.minTradeNumber());
if (need_back_shou > 0) {
size_t need_back_num = need_back_shou * stock.minTradeNumber();
size_t hold_num = sub_tm->getHoldNumber(date, stock);
if (hold_num - need_back_num < stock.minTradeNumber()) {
need_back_num = hold_num;
}
if (iter->sys->getParam<bool>("buy_delay")) {
delay_list.emplace_back(iter->sys, need_back_num);
} else {
auto tr =
iter->sys->sellForceOnClose(date, need_back_num, PART_ALLOCATEFUNDS);
if (!tr.isNull()) {
auto sub_cash = sub_tm->currentCash();
if (sub_tm->checkout(date, sub_cash)) {
m_cash_tm->checkin(date, sub_cash);
m_tm->addTradeRecord(tr); // 向总账户加入交易记录
}
// 获取当前最后的收盘价
price_t last_close_price = stock.getMarketValue(date, query.kType());
if (last_close_price <= 0.0) {
// 证券已失效,无法处理,资产全部损失
HKU_WARN_IF(trace, "{} has been delisted!", iter->sys->name());
continue;
}
double hold_num = sub_tm->getHoldNumber(date, stock);
if (hold_num <= 0.0) {
// 实际无持仓
continue;
}
// 预期需要卖出的数量
double min_num = stock.minTradeNumber();
double need_back_num =
static_cast<int64_t>(need_back_funds / last_close_price / min_num) * min_num;
if (hold_num - need_back_num < min_num) {
need_back_num = hold_num;
}
if (need_back_num == 0.0) {
continue;
}
if (iter->sys->getParam<bool>("buy_delay")) {
delay_list.emplace_back(iter->sys, need_back_num);
HKU_INFO_IF(trace, "[AF] Deduce delay {}, num: {}", iter->sys->name(),
need_back_num);
} else {
auto tr = iter->sys->sellForceOnClose(date, need_back_num, PART_ALLOCATEFUNDS);
if (!tr.isNull()) {
auto sub_cash = sub_tm->currentCash();
if (sub_tm->checkout(date, sub_cash)) {
m_cash_tm->checkin(date, sub_cash);
m_tm->addTradeRecord(tr); // 向总账户加入交易记录
HKU_INFO_IF(trace,
"[AF] Deduce position {}, sell num: {}, recycle cash: {}",
iter->sys->name(), need_back_num, sub_cash);
}
} else {
// 卖出失败的情况,也加入到延迟交易列表中
delay_list.emplace_back(iter->sys, need_back_num);
HKU_INFO_IF(trace, "[AF] Delay deduce position {}, need sell num: {}",
iter->sys->name(), need_back_num);
}
}
}
@ -383,13 +434,8 @@ SystemWeightList AllocateFundsBase::_adjust_with_running(
break;
}
// 计算实际可用的权重
price_t current_weight = iter->weight + sum_weight > can_allocate_weight
? iter->weight + sum_weight - can_allocate_weight
: iter->weight;
// 系统期望分配的资产额
price_t will_funds = roundUp(total_funds * current_weight, precision);
price_t will_funds = roundUp(total_funds * iter->weight, precision);
// 如果该系统是当前运行中系统
if (running_set.find(iter->sys) != running_set.cend()) {
@ -408,40 +454,35 @@ SystemWeightList AllocateFundsBase::_adjust_with_running(
// 未执行过减仓的系统,需要予以相应资金分配
if (sub_total_funds >= will_funds) {
sum_weight += sub_total_funds / total_funds;
} else {
price_t need_cash = will_funds - sub_total_funds;
if (need_cash > can_allocate_cash) {
need_cash = can_allocate_cash;
}
// 如果期望的资金连一手都买不起,则跳过
auto krecord = iter->sys->getStock().getKRecord(date, query.kType());
if (krecord.isValid() &&
need_cash < krecord.closePrice * iter->sys->getStock().minTradeNumber()) {
// 如果期望的资金连一手都买不起(含退市),则跳过
auto last_price = iter->sys->getStock().getMarketValue(date, query.kType());
if (need_cash < last_price * iter->sys->getStock().minTradeNumber()) {
continue;
}
if (m_cash_tm->checkout(date, need_cash)) {
sub_tm->checkin(date, need_cash);
HKU_INFO_IF(trace, "[AF] {} fetched cash: {}", iter->sys->name(),
need_cash);
can_allocate_cash = roundDown(can_allocate_cash - need_cash, precision);
// 更新已分配的累积权重
sum_weight += (sub_total_funds + need_cash) / total_funds;
}
}
}
} else {
// 非运行中的系统
// 计算子账户实际可获取的的资金
price_t need_cash = will_funds <= can_allocate_cash ? will_funds : can_allocate_cash;
// 如果期望的资金连一手都买不起,则跳过
const KQuery& query = iter->sys->getTO().getQuery();
auto krecord = iter->sys->getStock().getKRecord(date, query.kType());
if (krecord.isValid() &&
need_cash < krecord.closePrice * iter->sys->getStock().minTradeNumber()) {
continue;
}
// 尝试从资金账户中取出资金存入子账户
TMPtr sub_tm = iter->sys->getTM();
if (m_cash_tm->checkout(date, need_cash)) {

View File

@ -72,6 +72,8 @@ void Portfolio::paramChanged() {
void Portfolio::reset() {
m_real_sys_list.clear();
m_running_sys_set.clear();
m_failed_sys_map.clear();
m_dlist_sys_list.clear();
m_delay_adjust_sys_list.clear();
m_tmp_selected_list.clear();
m_tmp_will_remove_sys.clear();
@ -92,7 +94,7 @@ PortfolioPtr Portfolio::clone() {
p->m_name = m_name;
p->m_query = m_query;
p->m_real_sys_list = m_real_sys_list;
p->m_need_calculate = m_need_calculate;
p->m_need_calculate = true;
if (m_se)
p->m_se = m_se->clone();
if (m_af)
@ -195,9 +197,33 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) {
bool trace = getParam<bool>("trace");
HKU_INFO_IF(trace, "{} ===========================================================", date);
HKU_INFO_IF(trace && adjust, "[PF] Position adjustment will be made today.");
if (trace && adjust) {
HKU_INFO("****************************************************");
HKU_INFO("** **");
HKU_INFO("** [PF] Position adjustment will be made today. **");
HKU_INFO("** **");
HKU_INFO("****************************************************");
}
HKU_INFO_IF(trace, "[PF] current running system size: {}", m_running_sys_set.size());
//---------------------------------------------------
// 检测运行系统中是否存在已退市的证券
//---------------------------------------------------
for (auto iter = m_running_sys_set.begin(); iter != m_running_sys_set.end(); /*++iter*/) {
auto& sys = *iter;
if (sys->getStock().getMarketValue(date, m_query.kType()) == 0.0) {
auto sub_tm = sys->getTM();
auto sub_cash = sub_tm->currentCash();
if (sub_cash > 0.0 && sub_tm->checkout(date, sub_cash)) {
m_cash_tm->checkin(date, sub_cash);
}
m_dlist_sys_list.emplace_back(sys);
m_running_sys_set.erase(iter++);
} else {
++iter;
}
}
//---------------------------------------------------
// 开盘前处理各个子账户、资金账户、总账户之间可能的误差
//---------------------------------------------------
@ -241,11 +267,53 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) {
}
//----------------------------------------------------------------------
// 开盘时,优先处理上一交易日确定的需延迟调仓卖出的系统,在开盘时先卖出调整
// 开盘时,优先处理上一交易日强制清、减仓失败的系统
//----------------------------------------------------------------------
// for (auto iter = m_failed_sys_map.begin(); iter != m_failed_sys_map.end();) {
// const auto& sys = iter->first;
// Stock stk = sys->getStock();
// if (date > stk.lastDatetime()) {
// // 已退市
// HKU_WARN_IF(trace, "{} has been delisted!", sys->name());
// m_dlist_sys_list.emplace_back(sys);
// m_failed_sys_map.erase(iter++);
// continue;
// }
// auto tr = sys->sellForceOnOpen(date, iter->second, PART_PORTFOLIO);
// if (!tr.isNull()) {
// HKU_INFO_IF(trace, "[PF] Process pre-failed sys: {}", tr);
// m_tm->addTradeRecord(tr);
// // 卖出后,尝试将资金取出转移至影子总账户
// TMPtr sub_tm = sys->getTM();
// auto sub_cash = sub_tm->currentCash();
// if (sub_cash > 0.0 && sub_tm->checkout(date, sub_cash)) {
// m_cash_tm->checkin(date, sub_cash);
// }
// m_failed_sys_map.erase(iter++);
// continue;
// } else {
// // 如果实际已没有持仓,则将其从失败列表中移除
// PositionRecord position = sys->getTM()->getPosition(date, sys->getStock());
// if (position.number <= 0.0) {
// m_failed_sys_map.erase(iter++);
// continue;
// }
// }
// ++iter;
// }
//----------------------------------------------------------------------
// 开盘时,优先处理上一交易日遗留的延迟调仓卖出的系统
//----------------------------------------------------------------------
HKU_INFO_IF(trace, "[PF] process delay adjust sys, size: {}", m_delay_adjust_sys_list.size());
SystemWeightList tmp_continue_adjust_sys_list;
for (auto& sys : m_delay_adjust_sys_list) {
auto tr = sys.sys->sellForceOnOpen(date, sys.weight, PART_PORTFOLIO);
// HKU_DEBUG_IF(trace && tr.isNull(), "[PF] Failed to force sell: {}", sys.sys->name());
if (!tr.isNull()) {
HKU_INFO_IF(trace, "[PF] Delay adjust sell: {}", tr);
m_tm->addTradeRecord(tr);
@ -256,15 +324,23 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) {
if (sub_cash > 0.0 && sub_tm->checkout(date, sub_cash)) {
m_cash_tm->checkin(date, sub_cash);
}
} else {
// 强制卖出失败的情况下,如果当前仍有持仓,则需要下一交易日继续进行处理
PositionRecord position = sys.sys->getTM()->getPosition(date, sys.sys->getStock());
if (position.number > 0.0) {
tmp_continue_adjust_sys_list.emplace_back(sys);
// m_failed_sys_map[sys.sys] = sys.weight;
}
}
}
// 清空,避免循环至下一个非调仓日时被重复处理
m_delay_adjust_sys_list.clear();
m_delay_adjust_sys_list.swap(tmp_continue_adjust_sys_list);
//---------------------------------------------------------------
// 遍历当前运行中的子系统,如果已没有分配资金和持仓,则回收
//---------------------------------------------------------------
//---------------------------------------------------------------
// 遍历当前运行中的子系统,如果已没有分配资金和持仓,则回收
//---------------------------------------------------------------
#if 0
m_tmp_will_remove_sys.clear();
for (auto& running_sys : m_running_sys_set) {
Stock stock = running_sys->getStock();
@ -278,14 +354,14 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) {
min_cash = krecord.openPrice * stock.minTradeNumber();
}
// 如果系统的剩余资金小于交易一手的资金,则回收资金
// 如果系统的剩余资金小于交易一手的资金,则回收资金??? TODO 放到 AF 中进行处理不足一手的
if (cash != 0 && cash <= min_cash) {
sub_tm->checkout(date, cash);
m_cash_tm->checkin(date, cash);
HKU_INFO_IF(trace, "Recycle cash ({:<.2f}) from {}, current cash: {}", cash,
running_sys->name(), m_cash_tm->currentCash());
// 如果已经没有持仓,则回收
if (position.number == 0) {
if (position.number <= 0.0) {
m_tmp_will_remove_sys.emplace_back(running_sys, 0.);
HKU_INFO_IF(trace, "[PF] Recycle running sys: {}", running_sys->name());
}
@ -297,6 +373,7 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) {
HKU_INFO_IF(trace, "Recycling system {}", sub_sys.sys->name());
m_running_sys_set.erase(sub_sys.sys);
}
#endif
//---------------------------------------------------
// 调仓日,进行资金分配调整
@ -312,9 +389,18 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) {
}
// 资产分配算法调整各子系统资产分配
m_delay_adjust_sys_list = m_af->adjustFunds(date, m_tmp_selected_list, m_running_sys_set);
tmp_continue_adjust_sys_list =
m_af->adjustFunds(date, m_tmp_selected_list, m_running_sys_set);
// 如果选中的系统不在已有列表中,且账户已经被分配了资金,则将其加入运行系统,并执行
if (m_delay_adjust_sys_list.empty()) {
m_delay_adjust_sys_list.swap(tmp_continue_adjust_sys_list);
} else {
for (auto& sw : tmp_continue_adjust_sys_list) {
m_delay_adjust_sys_list.emplace_back(sw);
}
}
// 如果选中的系统不在已有列表中,且账户已经被分配了资金,则将其加入运行系统列表
for (auto& sys : m_tmp_selected_list) {
if (m_running_sys_set.find(sys.sys) == m_running_sys_set.end()) {
if (sys.sys->getTM()->cash(date, m_query.kType()) > 0.0) {

View File

@ -117,6 +117,8 @@ protected:
// 用于中间计算的临时数据
std::unordered_set<SYSPtr> m_running_sys_set;
std::unordered_map<SYSPtr, double> m_failed_sys_map; // 强制卖出失败的系统集合
SystemList m_dlist_sys_list; // 因证券退市,无法执行买入的系统(资产全部损失)
SystemWeightList m_delay_adjust_sys_list; // 延迟调仓卖出的系统列表
SystemWeightList m_tmp_selected_list;
SystemWeightList m_tmp_will_remove_sys;

View File

@ -20,7 +20,7 @@ namespace hku {
*/
struct HKU_API SystemWeight {
SystemPtr sys;
price_t weight{1.0};
double weight{1.0};
SystemWeight() = default;
SystemWeight(const SystemPtr& sys_, double weight_) : sys(sys_), weight(weight_) {}

View File

@ -707,11 +707,10 @@ TradeRecord System::_sellForce(const Datetime& date, double num, Part from, bool
_getRealSellPrice(krecord.datetime, on_open ? src_krecord.openPrice : src_krecord.closePrice);
double min_num = m_stock.minTradeNumber();
double real_sell_num = num;
if (real_sell_num >= position.number) {
// 对待卖出的数量进行最小交易单位取整数倍,如果剩余不足最小交易单位,则一次全部卖出
double real_sell_num = static_cast<int64_t>(num / min_num) * min_num;
if (position.number - real_sell_num < min_num) {
real_sell_num = position.number;
} else if (min_num > 1) {
real_sell_num = static_cast<int64_t>(num / min_num) * min_num;
}
record =