在进行量化交易回测的过程中,我们总会遇到一些净值如何模拟的问题,尤其是带止盈止损的订单。
注:
1.本次回测的代码忽略了一些情况,是因为笔者本次不会遇到这些情况,但如果你需要,可以在此基础上稍作修改;
2.本次代码使用C++编写,如果你希望在python中实现,只需要编译为二进制文件即可(当然,如果你使用的不是python3.11,那么需要对代码进行一些修改)。
多的不说,直接上代码。
#include <iostream>
#include <vector>
#include <map>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
/*
conds的表示
+0.000:空仓
+0.010:多头持仓
-0.010:空头持仓
+0.100:多头开仓
-0.100:空头开仓
+0.001:多头平仓
-0.001:空头平仓
+0.101:多头开仓平仓(本次不考虑)
-0.101:空头开仓平仓(本次不考虑)
小数点后一位:是否有开仓动作
小数点后两位:是否全程持仓
小数点后三位:是否有平仓动作
*/
struct PLSInput{
double principal;
double proportion;
double MakerFee;
double TakerFee;
unsigned int leverage;
std::vector<int> signal; //开仓信号(0,1,-1)
std::vector<double> open; //开仓价
std::vector<double> close; //收盘价
std::vector<double> high;
std::vector<double> low;
std::vector<double> ps; //止盈点
std::vector<double> ls; //止损点
std::vector<size_t> remain; //最大持续时长
bool useable(){
if (signal.size() != open.size()) return false;
if (signal.size() != close.size()) return false;
if (signal.size() != ps.size()) return false;
if (signal.size() != ls.size()) return false;
if (signal.size() != remain.size()) return false;
return true;
}
};
struct result{
std::vector<double> net_value;
std::vector<double> conds;
};
result plsValue(PLSInput plsinput){
// 初始化变量
const size_t n = plsinput.signal.size();
result orders;
std::vector<double> net_value(n, plsinput.principal);
std::vector<double> conds(n, 0.0);
// 判断输入是否正确
if (!plsinput.useable()){
orders.conds = conds;
orders.net_value = net_value;
return orders;
}
// 输入正确后,处理程序
for (size_t i=1; i<n; i++){
double net = net_value[i - 1];
// 无信号
if (plsinput.signal[i] == 0){
conds[i] = 0.000;
net_value[i] = net;
}
// 多头信号
else if (plsinput.signal[i] == 1){
// 计算开仓数额,浮动与固定
const double open_price = plsinput.open[i];
const int quants = static_cast<int>(net * plsinput.leverage * plsinput.proportion / open_price);
const double fixed = net - quants * open_price * plsinput.MakerFee;
const double ps = plsinput.ps[i];
const double ls = plsinput.ls[i];
double floated = quants * (open_price - plsinput.close[i]);
conds[i] = 0.100;
net_value[i] = floated + fixed;
size_t j = 1;
while (j <= plsinput.remain[i]){
if (plsinput.low[i + j] >= ls){
floated = quants * (ls - open_price);
conds[i + j] = 0.001;
net_value[i + j] = floated + fixed - quants * ls * (1 - plsinput.TakerFee);
j++;
break;
}
else if (plsinput.high[i + j] <= ps){
floated = quants * (ps - open_price);
conds[i + j] = 0.001;
net_value[i + j] = floated + fixed - quants * ps * (1 - plsinput.TakerFee);
j++;
break;
}
else{
floated = quants * (plsinput.close[i + j] - open_price);
conds[i + j] = 0.010;
net_value[i + j] = j < plsinput.remain[i] ? floated + fixed : floated + fixed - quants * plsinput.close[i + j] * (1 - plsinput.TakerFee);
j++;
if (i + j >= n) break;
}
}
i = i + j;
}
// 空头信号
else{
// 计算开仓数额,浮动与固定
const double open_price = plsinput.open[i];
const int quants = static_cast<int>(net * plsinput.leverage * plsinput.proportion / open_price);
const double fixed = net - quants * open_price * plsinput.MakerFee;
const double ps = plsinput.ps[i];
const double ls = plsinput.ls[i];
double floated = quants * (open_price - plsinput.close[i]) * (-1.0);
conds[i] = -0.100;
net_value[i] = floated + fixed;
size_t j = 1;
while (j <= plsinput.remain[i]){
if (plsinput.low[i + j] >= ls){
floated = quants * (ls - open_price) * (-1.0);
conds[i + j] = -0.001;
net_value[i + j] = floated + fixed - quants * ls * (1 - plsinput.TakerFee);
j++;
break;
}
else if (plsinput.high[i + j] <= ps){
floated = quants * (ps - open_price) * (-1.0);
conds[i + j] = -0.001;
net_value[i + j] = floated + fixed - quants * ps * (1 - plsinput.TakerFee);
j++;
break;
}
else{
floated = quants * (plsinput.close[i + j] - open_price) * (-1.0);
conds[i + j] = -0.010;
net_value[i + j] = j < plsinput.remain[i] ? floated + fixed : floated + fixed - quants * plsinput.close[i + j] * (1 - plsinput.TakerFee);
j++;
if (i + j >= n) break;
}
}
i = i + j;
}
}
orders.conds = conds;
orders.net_value = net_value;
return orders;
}
namespace py = pybind11;
PYBIND11_MODULE(plsValue, m) {
m.def("plsValue", &plsValue, "plsValue");
}
原创代码,笔者水平有限,且目前代码还未经过大量检验,如有瑕疵,敬请谅解。如有需要,可以与笔者交流。
邮箱:ldy675391321@gmail.com
作者:DINO