tzutrader
A composable C++ backtesting library for trading strategies (experimental)
Loading...
Searching...
No Matches
portfolios.h
Go to the documentation of this file.
1#ifndef PORTFOLIOS_H
2#define PORTFOLIOS_H
3
4#include <cstdint>
5#include <cmath>
6#include <iostream>
7#include <vector>
8#include <iomanip>
9#include "defs.h"
10#include "stats.h"
11
12namespace tzu {
13
14template<class T>
15class Portfolio {
16public:
17 void update(const T& signal) {
18 static_cast<T*>(this)->update(signal);
19 }
20};
21
22
35class BasicPortfolio: public Portfolio<BasicPortfolio> {
36private:
37 double init_cash;
38 double cash;
39 std::vector<Position> positions;
40 double tx_cost_pct;
41 double stop_loss_pct;
42 double take_profit_pct;
43 double last_price = std::nan("");
44 PortfolioStats stats;
45
46 void liquidate_position_at(size_t i, double price, int64_t timestamp,
47 bool is_stop_loss = false, bool is_take_profit = false) {
48 const Position& pos = positions[i];
49 double proceeds = pos.quantity * price;
50 double commission = proceeds * tx_cost_pct;
51 stats.add_costs(commission);
52 cash += proceeds - commission;
53
54 double profit = (price - pos.price) * pos.quantity - commission;
55 stats.record_trade_close(timestamp, pos.quantity, pos.price, price, profit,
56 is_stop_loss, is_take_profit);
57
58 positions[i] = positions.back();
59 positions.pop_back();
60 }
61
62 void liquidate_all_at_price(double price, int64_t timestamp) {
63 for (const auto& p : positions) {
64 double proceeds = p.quantity * price;
65 double commission = proceeds * tx_cost_pct;
66 stats.add_costs(commission);
67 cash += proceeds - commission;
68
69 double profit = (price - p.price) * p.quantity - commission;
70 stats.record_trade_close(timestamp, p.quantity, p.price, price, profit, false, false);
71 }
72 positions.clear();
73 }
74
75 double compute_holdings_value() const {
76 double holdings = 0.0;
77 for (const auto& p : positions) {
78 holdings += p.quantity * last_price;
79 }
80 return holdings;
81 }
82
83 double compute_total_quantity() const {
84 double qty = 0.0;
85 for (const auto& p : positions) {
86 qty += p.quantity;
87 }
88 return qty;
89 }
90
91 double compute_total_value() const {
92 return cash + compute_holdings_value();
93 }
94
95 void check_stop_loss_take_profit(double current_price, int64_t timestamp) {
96 for (size_t i = 0; i < positions.size();) {
97 bool should_liquidate = false;
98 bool is_stop_loss = false;
99 bool is_take_profit = false;
100 const Position& p = positions[i];
101
102 if (!std::isnan(stop_loss_pct)) {
103 double stop_price = p.price * (1.0 - stop_loss_pct);
104 if (current_price <= stop_price) {
105 should_liquidate = true;
106 is_stop_loss = true;
107 }
108 }
109
110 if (!should_liquidate && !std::isnan(take_profit_pct)) {
111 double tp_price = p.price * (1.0 + take_profit_pct);
112 if (current_price >= tp_price) {
113 should_liquidate = true;
114 is_take_profit = true;
115 }
116 }
117
118 if (should_liquidate) {
119 stats.increment_trades();
120 liquidate_position_at(i, current_price, timestamp, is_stop_loss, is_take_profit);
121 } else {
122 ++i;
123 }
124 }
125 }
126
127 void process_signal(const Signal& signal) {
128 if (signal.side == Side::BUY) {
129 execute_buy(signal);
130 } else if (signal.side == Side::SELL) {
131 execute_sell(signal);
132 }
133 }
134
135 void execute_buy(const Signal& signal) {
136 double unit_cost = signal.price * (1.0 + tx_cost_pct);
137 double qty = std::floor(cash / unit_cost);
138 if (qty > 0) {
139 double cost = qty * signal.price;
140 double commission = cost * tx_cost_pct;
141 stats.add_costs(commission);
142 cash -= cost + commission;
143 stats.increment_trades();
144 stats.record_trade_open(signal.timestamp, qty, signal.price);
145 positions.push_back(Position{signal.timestamp, qty, signal.price});
146 }
147 }
148
149 void execute_sell(const Signal& signal) {
150 size_t num_positions = positions.size();
151 if (num_positions > 0) {
152 stats.increment_trades();
153 liquidate_all_at_price(signal.price, signal.timestamp);
154 }
155 }
156
157 double get_holdings_value() const {
158 return compute_holdings_value();
159 }
160
161 double get_total_quantity() const {
162 return compute_total_quantity();
163 }
164
165public:
166 BasicPortfolio(double initCash = 100000.0,
167 double txCostPct = 0.0,
168 double stopLossPct = std::nan(""),
169 double takeProfitPct = std::nan(""))
170 : init_cash(initCash), cash(initCash),
171 tx_cost_pct(txCostPct), stop_loss_pct(stopLossPct),
172 take_profit_pct(takeProfitPct) {}
173
174 void update(const Signal& signal) {
175 if (signal.price <= 0.0) return;
176 last_price = signal.price;
177
178 if (stats.is_initialized()) {
179 stats.initialize(signal.timestamp, cash, signal.price);
180 }
181
182 check_stop_loss_take_profit(signal.price, signal.timestamp);
183 process_signal(signal);
184
185 stats.record_equity(signal.timestamp, compute_total_value(), signal.price);
186 }
187
188 friend std::ostream& operator<<(std::ostream& os,
189 const BasicPortfolio& portfolio);
190};
191
192inline std::ostream& operator<<(std::ostream& os, const BasicPortfolio& portfolio) {
193 double holdings = portfolio.get_holdings_value();
194 double qty = portfolio.get_total_quantity();
195 double total_value = portfolio.compute_total_value();
196
197 portfolio.stats.print_summary(os, portfolio.cash, holdings, qty, total_value);
198
199 return os;
200}
201
202} // namespace tzu
203
204#endif // PORTFOLIOS_H
Definition portfolios.h:35
friend std::ostream & operator<<(std::ostream &os, const BasicPortfolio &portfolio)
Definition portfolios.h:192
void update(const Signal &signal)
Definition portfolios.h:174
BasicPortfolio(double initCash=100000.0, double txCostPct=0.0, double stopLossPct=std::nan(""), double takeProfitPct=std::nan(""))
Definition portfolios.h:166
Definition stats.h:12
void print_summary(std::ostream &os, double curr_cash, double holdings, double qty, double total_value) const
Definition stats.h:216
Definition portfolios.h:15
void update(const T &signal)
Definition portfolios.h:17
Core data structures and types for the tzutrader library.
Definition defs.h:20
std::ostream & operator<<(std::ostream &os, const BasicPortfolio &portfolio)
Definition portfolios.h:192
@ SELL
Sell signal or short position.
Definition defs.h:30
@ BUY
Buy signal or long position.
Definition defs.h:29
Definition defs.h:157
double quantity
Definition defs.h:159
double price
Definition defs.h:160
Trading signal generated by a strategy.
Definition defs.h:44
double price
Price at which to execute the trade.
Definition defs.h:47
int64_t timestamp
Unix timestamp when signal was generated.
Definition defs.h:45
Side side
Trade direction (BUY/SELL/NONE).
Definition defs.h:46