买空/卖空支持(continue)

This commit is contained in:
fasiondog 2022-09-11 01:14:20 +08:00
parent 87ffded746
commit 17ae2a3261
4 changed files with 60 additions and 159 deletions

View File

@ -10,46 +10,16 @@
namespace hku {
PositionRecord::PositionRecord(PositionRecord&& rv) {
stock = rv.stock;
takeDatetime = rv.takeDatetime;
cleanDatetime = rv.cleanDatetime;
number = rv.number;
stoploss = rv.stoploss;
goalPrice = rv.goalPrice;
totalNumber = rv.totalNumber;
buyMoney = rv.buyMoney;
totalCost = rv.totalCost;
totalRisk = rv.totalRisk;
sellMoney = rv.sellMoney;
contracts = std::move(rv.contracts);
}
PositionRecord& PositionRecord::operator=(PositionRecord&& rv) {
HKU_IF_RETURN(this == &rv, *this);
stock = rv.stock;
takeDatetime = rv.takeDatetime;
cleanDatetime = rv.cleanDatetime;
number = rv.number;
stoploss = rv.stoploss;
goalPrice = rv.goalPrice;
totalNumber = rv.totalNumber;
buyMoney = rv.buyMoney;
totalCost = rv.totalCost;
totalRisk = rv.totalRisk;
sellMoney = rv.sellMoney;
contracts = std::move(rv.contracts);
return *this;
}
PositionRecord::PositionRecord(const Stock& stock, const Datetime& takeDatetime,
const Datetime& cleanDatetime, double number, price_t stoploss,
price_t goalPrice, double totalNumber, price_t totalMoney,
price_t totalCost, price_t totalRisk, price_t sellMoney)
const Datetime& cleanDatetime, double number, price_t avgPrice,
price_t stoploss, price_t goalPrice, double totalNumber,
price_t totalMoney, price_t totalCost, price_t totalRisk,
price_t sellMoney)
: stock(stock),
takeDatetime(takeDatetime),
cleanDatetime(cleanDatetime),
number(number),
avgPrice(avgPrice),
stoploss(stoploss),
goalPrice(goalPrice),
totalNumber(totalNumber),
@ -64,57 +34,48 @@ void PositionRecord::addTradeRecord(const TradeRecord& tr, const MarginRecord& m
takeDatetime = tr.datetime;
}
switch (tr.business) {
case BUSINESS_BUY:
number += tr.number;
case BUSINESS_BUY: {
double new_number = number + tr.number;
if (new_number == 0.0) {
cleanDatetime = tr.datetime;
} else {
avgPrice = std::fabs((tr.realPrice * tr.number + avgPrice * number) / new_number);
number = new_number;
stoploss = tr.stoploss;
goalPrice = tr.goalPrice;
totalNumber += tr.number;
buyMoney =
roundEx(tr.realPrice * tr.number * stock.unit() * tr.margin_ratio + buyMoney,
stock.precision());
totalCost = roundEx(tr.cost.total + totalCost, stock.precision());
totalRisk =
roundEx(totalRisk + (tr.realPrice - tr.stoploss) * number * stock.unit(),
stock.precision());
}
} break;
case BUSINESS_SELL: {
double new_number = number - tr.number;
if (new_number == 0.0) {
cleanDatetime = tr.datetime;
} else {
avgPrice = std::fabs((tr.realPrice * tr.number - avgPrice * number) / new_number);
}
number = new_number;
stoploss = tr.stoploss;
goalPrice = tr.goalPrice;
totalNumber += tr.number;
buyMoney =
roundEx(tr.realPrice * tr.number * stock.unit() + buyMoney, stock.precision());
totalCost = roundEx(tr.cost.total + totalCost, stock.precision());
totalRisk = roundEx(totalRisk + (tr.realPrice - tr.stoploss) * number * stock.unit(),
stock.precision());
break;
case BUSINESS_SELL:
number -= tr.number;
if (number == 0.0) {
cleanDatetime = tr.datetime;
}
stoploss = tr.stoploss;
goalPrice = tr.goalPrice;
totalCost = roundEx(tr.cost.total + totalCost, stock.precision());
sellMoney =
roundEx(sellMoney + tr.realPrice * tr.number * stock.unit(), stock.precision());
break;
} break;
default:
HKU_ERROR("The business({}) should not appear here!", getBusinessName(tr.business));
break;
}
contracts.emplace_back(tr.datetime, tr.business, tr.realPrice, tr.number, 0.0, margin);
}
std::tuple<price_t, price_t> PositionRecord::getProfit(Datetime datetime) {
price_t profit = 0.0; // 浮动盈亏
price_t margin = 0.0; // 维持保证金
size_t pos = stock.getPos(datetime);
if (pos != 0 && pos != Null<size_t>()) {
for (auto& contract : contracts) {
auto k = stock.getKRecord(pos - 1);
contract.profit = (k.closePrice - contract.price) * number * stock.unit();
profit += contract.profit;
margin += k.closePrice * number * stock.unit() * contract.margin.initRatio *
contract.margin.maintainRatio;
}
} else {
for (auto& contract : contracts) {
profit += contract.profit;
margin += contract.price * contract.number * stock.unit() * contract.margin.initRatio *
contract.margin.maintainRatio;
}
}
return std::make_tuple(profit, margin);
}
string PositionRecord::toString() const {
@ -128,17 +89,12 @@ string PositionRecord::toString() const {
precision = stock.precision();
}
price_t costPrice = 0.0;
if (number != 0.0) {
costPrice = roundEx((buyMoney - sellMoney) / number, precision);
}
string strip(", ");
std::stringstream os;
os << std::fixed;
os.precision(precision);
os << "Position(" << market << strip << code << strip << name << strip << takeDatetime << strip
<< cleanDatetime << strip << number << strip << costPrice << strip << stoploss << strip
<< cleanDatetime << strip << number << strip << avgPrice << strip << stoploss << strip
<< goalPrice << strip << totalNumber << strip << buyMoney << strip << totalCost << strip
<< totalRisk << strip << sellMoney << ")";
os.unsetf(std::ostream::floatfield);
@ -149,7 +105,8 @@ string PositionRecord::toString() const {
bool HKU_API operator==(const PositionRecord& d1, const PositionRecord& d2) {
return d1.stock == d2.stock && d1.takeDatetime == d2.takeDatetime &&
d1.cleanDatetime == d2.cleanDatetime && fabs(d1.number - d2.number) < 0.00001 &&
fabs(d1.stoploss - d2.stoploss) < 0.0001 && fabs(d1.goalPrice - d2.goalPrice) < 0.0001 &&
fabs(d1.avgPrice - d2.avgPrice) < 0.0001 && fabs(d1.stoploss - d2.stoploss) < 0.0001 &&
fabs(d1.goalPrice - d2.goalPrice) < 0.0001 &&
fabs(d1.totalNumber - d2.totalNumber) < 0.00001 &&
fabs(d1.buyMoney - d2.buyMoney) < 0.0001 && fabs(d1.totalCost - d2.totalCost) < 0.0001 &&
fabs(d1.sellMoney - d2.sellMoney) < 0.0001;

View File

@ -19,39 +19,6 @@
namespace hku {
struct ContractRecord {
ContractRecord() = default;
ContractRecord(const Datetime& datetime, BUSINESS business, price_t price, double number,
price_t profit, const MarginRecord& margin)
: datetime(datetime),
business(business),
price(price),
number(number),
profit(profit),
margin(margin) {}
Datetime datetime; // 交易日期
BUSINESS business; // 业务类型
price_t price; // 成交价格
double number; // 成交数量
price_t profit; // 浮动盈亏
MarginRecord margin; // 保证金比例
#if HKU_SUPPORT_SERIALIZATION
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive& ar, const unsigned int version) {
ar& BOOST_SERIALIZATION_NVP(datetime);
ar& BOOST_SERIALIZATION_NVP(business);
ar& BOOST_SERIALIZATION_NVP(price);
ar& BOOST_SERIALIZATION_NVP(number);
ar& BOOST_SERIALIZATION_NVP(profit);
ar& BOOST_SERIALIZATION_NVP(margin);
}
#endif /* HKU_SUPPORT_SERIALIZATION */
};
/**
*
* @ingroup TradeManagerClass
@ -59,14 +26,10 @@ private:
class HKU_API PositionRecord {
public:
PositionRecord() = default;
PositionRecord(const PositionRecord&) = default;
PositionRecord(PositionRecord&& rv);
PositionRecord& operator=(const PositionRecord&) = default;
PositionRecord& operator=(PositionRecord&& rv);
PositionRecord(const Stock& stock, const Datetime& takeDatetime, const Datetime& cleanDatetime,
double number, price_t stoploss, price_t goalPrice, double totalNumber,
price_t buyMoney, price_t totalCost, price_t totalRisk, price_t sellMoney);
double number, price_t avgPrice, price_t stoploss, price_t goalPrice,
double totalNumber, price_t buyMoney, price_t totalCost, price_t totalRisk,
price_t sellMoney);
/** 仅用于python的__str__ */
string toString() const;
@ -81,6 +44,7 @@ public:
Datetime takeDatetime; ///< 初次建仓日期
Datetime cleanDatetime; ///< 平仓日期当前持仓记录中为Null<Datetime>()
double number = 0.0; ///< 当前持仓数量
price_t avgPrice = 0.0; ///< 平均买入/卖出价格
price_t stoploss = 0.0; ///< 当前止损价
price_t goalPrice = 0.0; ///< 当前的目标价格
double totalNumber = 0.0; ///< 累计持仓数量
@ -89,8 +53,6 @@ public:
price_t totalRisk = 0.0; ///< 累计交易风险 = 各次 (买入价格-止损)*买入数量, 不包含交易成本
price_t sellMoney = 0.0; ///< 累计卖出资金
std::list<ContractRecord> contracts;
//===================
//序列化支持
//===================
@ -106,6 +68,7 @@ private:
ar& bs::make_nvp("takeDatetime", take);
ar& bs::make_nvp("cleanDatetime", clean);
ar& BOOST_SERIALIZATION_NVP(number);
ar& BOOST_SERIALIZATION_NVP(avgPrice);
ar& BOOST_SERIALIZATION_NVP(stoploss);
ar& BOOST_SERIALIZATION_NVP(goalPrice);
ar& BOOST_SERIALIZATION_NVP(totalNumber);
@ -113,7 +76,6 @@ private:
ar& BOOST_SERIALIZATION_NVP(totalCost);
ar& BOOST_SERIALIZATION_NVP(totalRisk);
ar& BOOST_SERIALIZATION_NVP(sellMoney);
ar& BOOST_SERIALIZATION_NVP(contracts);
}
template <class Archive>
@ -126,6 +88,7 @@ private:
takeDatetime = Datetime(take);
cleanDatetime = Datetime(clean);
ar& BOOST_SERIALIZATION_NVP(number);
ar& BOOST_SERIALIZATION_NVP(avgPrice);
ar& BOOST_SERIALIZATION_NVP(stoploss);
ar& BOOST_SERIALIZATION_NVP(goalPrice);
ar& BOOST_SERIALIZATION_NVP(totalNumber);
@ -133,7 +96,6 @@ private:
ar& BOOST_SERIALIZATION_NVP(totalCost);
ar& BOOST_SERIALIZATION_NVP(totalRisk);
ar& BOOST_SERIALIZATION_NVP(sellMoney);
ar& BOOST_SERIALIZATION_NVP(contracts);
}
BOOST_SERIALIZATION_SPLIT_MEMBER()

View File

@ -318,13 +318,12 @@ TradeRecord TradeManager::buy(const Datetime& datetime, const Stock& stock, pric
HKU_ERROR_IF_RETURN(datetime < lastDatetime(), result,
"{} {} datetime must be >= lastDatetime({})!", datetime,
stock.market_code(), lastDatetime());
HKU_ERROR_IF_RETURN(number == 0.0 || (!getParam<bool>("support_margin") && number <= 0.0),
result, "{} {} Invalid buy numer: {}!", datetime, stock.market_code(),
number);
HKU_ERROR_IF_RETURN(std::fabs(number) < stock.minTradeNumber(), result,
HKU_ERROR_IF_RETURN(number <= 0.0, result, "{} {} Invalid buy numer: {}!", datetime,
stock.market_code(), number);
HKU_ERROR_IF_RETURN(number < stock.minTradeNumber(), result,
"{} {} Buy number({}) must be >= minTradeNumber({})!", datetime,
stock.market_code(), number, stock.minTradeNumber());
HKU_ERROR_IF_RETURN(std::fabs(number) > stock.maxTradeNumber(), result,
HKU_ERROR_IF_RETURN(number != MAX_DOUBLE && number > stock.maxTradeNumber(), result,
"{} {} Buy number({}) must be <= maxTradeNumber({})!", datetime,
stock.market_code(), number, stock.maxTradeNumber());
@ -387,6 +386,11 @@ TradeRecord TradeManager::buy(const Datetime& datetime, const Stock& stock, pric
} else {
PositionRecord& position = pos_iter->second;
position.addTradeRecord(result, margin);
if (position.number == 0.0) {
m_position_history.push_back(position);
//删除当前持仓
m_position.erase(stock.id());
}
}
if (result.datetime > m_broker_last_datetime) {
@ -413,7 +417,7 @@ TradeRecord TradeManager::sell(const Datetime& datetime, const Stock& stock, pri
HKU_ERROR_IF_RETURN(datetime < lastDatetime(), result,
"{} {} datetime must be >= lastDatetime({})!", datetime,
stock.market_code(), lastDatetime());
HKU_ERROR_IF_RETURN(number == 0.0, result, "{} {} number is zero!", datetime,
HKU_ERROR_IF_RETURN(number <= 0.0, result, "{} {} number is zero!", datetime,
stock.market_code());
// 对于分红扩股造成不满足最小交易量整数倍的情况只能通过number=MAX_DOUBLE的方式全仓卖出
@ -737,29 +741,6 @@ void TradeManager::updateWithWeight(const Datetime& datetime) {
m_trade_list.push_back(new_trade_buffer[i]);
}
if (getParam<bool>("support_margin")) {
price_t total_profit = 0.0;
price_t total_maintain_margin = 0.0;
for (; position_iter != m_position.end(); ++position_iter) {
auto [profit, margin] = position_iter->second.getProfit(datetime);
total_profit += profit;
total_maintain_margin += margin;
}
bool will_margin_closeout = false;
if (total_profit < 0.0 && m_cash < total_maintain_margin) {
if (getParam<bool>("auto_checkin")) {
checkin(datetime, roundEx(total_maintain_margin - m_cash, precision));
} else {
will_margin_closeout = true;
}
}
if (will_margin_closeout) {
// 执行强平操作
}
}
m_last_update_datetime = datetime;
}
@ -996,8 +977,8 @@ bool TradeManager::_add_buy_tr(const TradeRecord& tr) {
position_map_type::iterator pos_iter = m_position.find(tr.stock.id());
if (pos_iter == m_position.end()) {
m_position[tr.stock.id()] = PositionRecord(
tr.stock, tr.datetime, Null<Datetime>(), tr.number, tr.stoploss, tr.goalPrice, tr.number,
money, tr.cost.total,
tr.stock, tr.datetime, Null<Datetime>(), tr.number, tr.realPrice, tr.stoploss,
tr.goalPrice, tr.number, money, tr.cost.total,
roundEx((tr.realPrice - tr.stoploss) * tr.number * tr.stock.unit(), precision), 0.0);
} else {
PositionRecord& position = pos_iter->second;

View File

@ -19,8 +19,8 @@ using namespace hku;
void export_PositionRecord() {
class_<PositionRecord>("PositionRecord", "持仓记录", init<>())
.def(init<const Stock&, const Datetime&, const Datetime&, double, price_t, price_t, double,
price_t, price_t, price_t, price_t>())
.def(init<const Stock&, const Datetime&, const Datetime&, double, price_t, price_t, price_t,
double, price_t, price_t, price_t, price_t>())
.def("__str__", &PositionRecord::toString)
.def("__repr__", &PositionRecord::toString)
@ -30,6 +30,7 @@ void export_PositionRecord() {
.def_readwrite("clean_datetime", &PositionRecord::cleanDatetime,
"平仓日期,当前持仓记录中为 constant.null_datetime")
.def_readwrite("number", &PositionRecord::number, "当前持仓数量float")
.def_readwrite("avg_price", &PositionRecord::avgPrice, "平均买入/卖出价格(float)")
.def_readwrite("stoploss", &PositionRecord::stoploss, "当前止损价float")
.def_readwrite("goal_price", &PositionRecord::goalPrice, "当前的目标价格float")
.def_readwrite("total_number", &PositionRecord::totalNumber, "累计持仓数量float")