FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
pineforge-engine/src/engine_risk.cpp at main · pineforge-4pass/pineforge-engine · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
pineforge-4pass
/
pineforge-engine
Public
Notifications
You must be signed in to change notification settings
Fork
42
Star
179
Code
Issues
1
Pull requests
1
Discussions
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Discussions
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
pineforge-engine
/
src
/
engine_risk.cpp
Copy path
More file actions
More file actions
Latest commit
History
History
History
151 lines (138 loc) · 6.12 KB
Breadcrumbs
pineforge-engine
/
src
/
engine_risk.cpp
Copy path
File metadata and controls
151 lines (138 loc) · 6.12 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
* engine_risk.cpp — risk management + per-trade extreme tracking.
*
* Carved out of engine.cpp during the v0.1 file-split (phase 6) so
* the BacktestEngine implementation becomes navigable.
*
* check_risk_allow_entry - gate entries by direction / position cap / halt
* update_risk_state - check drawdown / intraday loss / consecutive
* loss thresholds; latch risk_halted_ when hit
* update_per_trade_extremes - per-pyramid-entry MFE/MAE tracking from H/L/C
*
* All functions are BacktestEngine instance methods; they access the
* engine's private state declared in <pineforge/engine.hpp>.
*/
#
include
<
pineforge/engine.hpp
>
#
include
<
ctime
>
#
include
"
timezone.hpp
"
namespace
pineforge
{
//
See declaration in include/pineforge/engine.hpp. Used only by the
//
intraday-day rollover gates below and the analogous gates in
//
engine_fills.cpp / engine_orders.cpp. When ``chart_timezone_`` is
//
empty we keep the cheap UTC fast path; otherwise we route through
//
``ScopedTimezone`` + ``localtime_r`` so IANA names like "Asia/Taipei"
//
resolve correctly (POSIX-numeric offsets inside the same string syntax
//
would silently disagree with the rest of the engine's TZ handling).
BacktestEngine::BarTime
BacktestEngine::_decompose_bar_time_chart_tz
()
const
{
if
(chart_timezone_.
empty
() || chart_timezone_ ==
"
UTC
"
||
chart_timezone_ ==
"
Etc/UTC
"
) {
return
_decompose_bar_time
();
}
time_t
secs =
static_cast
<
time_t
>(current_bar_.
timestamp
/
1000
);
struct
tm
tm_buf {};
{
pine_tz::ScopedTimezone
guard
(chart_timezone_);
localtime_r
(&secs, &tm_buf);
}
BarTime bt;
bt.
year
= tm_buf.
tm_year
+
1900
;
bt.
month
= tm_buf.
tm_mon
+
1
;
bt.
dayofmonth
= tm_buf.
tm_mday
;
bt.
hour
= tm_buf.
tm_hour
;
bt.
minute
= tm_buf.
tm_min
;
bt.
second
= tm_buf.
tm_sec
;
bt.
dayofweek
= tm_buf.
tm_wday
+
1
;
bt.
weekofyear
= (tm_buf.
tm_yday
+
7
- ((tm_buf.
tm_wday
+
6
) %
7
)) /
7
;
return
bt;
}
bool
BacktestEngine::check_risk_allow_entry
(
bool
is_long)
const
{
if
(risk_halted_)
return
false
;
if
(risk_direction_ == RiskDirection::
LONG_ONLY
&& !is_long)
return
false
;
if
(risk_direction_ == RiskDirection::
SHORT_ONLY
&& is_long)
return
false
;
if
(risk_max_position_size_ >
0.0
&& position_qty_ >= risk_max_position_size_)
return
false
;
return
true
;
}
void
BacktestEngine::update_risk_state
() {
if
(risk_halted_)
return
;
//
Check max_drawdown
if
(risk_max_drawdown_ >
0.0
) {
double
threshold = risk_max_drawdown_;
if
(risk_max_drawdown_is_pct_) {
//
percent_of_equity: threshold is pct% of peak equity
threshold = max_equity_ * (risk_max_drawdown_ /
100.0
);
}
if
(max_drawdown_ >= threshold) {
risk_halted_ =
true
;
return
;
}
}
//
Check max_intraday_loss
if
(risk_max_intraday_loss_ >
0.0
) {
BarTime bt =
_decompose_bar_time_chart_tz
();
int
cur_day = bt.
dayofmonth
*
100
+ bt.
month
;
if
(cur_day != intraday_pnl_day_) {
intraday_pnl_day_ = cur_day;
intraday_pnl_ =
0.0
;
}
double
intraday_threshold = risk_max_intraday_loss_;
if
(risk_max_intraday_loss_is_pct_) {
double
eq = initial_capital_ + net_profit_sum_ +
open_profit
(current_bar_.
close
);
intraday_threshold = eq * (risk_max_intraday_loss_ /
100.0
);
}
if
(intraday_pnl_ <
0.0
&& (-intraday_pnl_) >= intraday_threshold) {
risk_halted_ =
true
;
return
;
}
}
//
Check max_cons_loss_days
if
(risk_max_cons_loss_days_ >
0
&& cons_loss_day_count_ >= risk_max_cons_loss_days_) {
risk_halted_ =
true
;
return
;
}
}
//
Tracks favorable (max_runup / MFE) and adverse (max_drawdown / MAE) price
//
excursion per open pyramid entry.
//
//
We sample three representative prices per call — high, low, close — so a
//
single daily bar fully captures both extremes without requiring tick-level
//
resolution. During bar magnifier the high/low are running_high/running_low
//
of the sampled path so no double-counting occurs, and close is the current
//
sampled price.
void
BacktestEngine::update_per_trade_extremes
() {
bool
is_long = (position_side_ == PositionSide::
LONG
);
double
hi = current_bar_.
high
;
double
lo = current_bar_.
low
;
double
cl = current_bar_.
close
;
for
(
auto
& pe : pyramid_entries_) {
//
Intrabar-fill masks: on the bar a priced entry filled mid-bar, an
//
extreme that the assumed OHLC path reaches BEFORE the fill is not
//
part of this trade's excursion — substitute the fill price (zero
//
excursion) for that extreme. Post-fill path beyond the masked
//
extreme is still captured by the close sample below. Later bars
//
(entry_bar_index != bar_index_) always sample the full range.
double
pe_hi = hi;
double
pe_lo = lo;
if
(pe.
entry_bar_index
== bar_index_) {
if
(pe.
skip_entry_bar_high
) pe_hi = pe.
price
;
if
(pe.
skip_entry_bar_low
) pe_lo = pe.
price
;
}
//
Favorable price: long -> high, short -> low
double
fav_px = is_long ? pe_hi : pe_lo;
double
adv_px = is_long ? pe_lo : pe_hi;
double
favorable = is_long ? (fav_px - pe.
price
) * pe.
qty
: (pe.
price
- fav_px) * pe.
qty
;
double
adverse = is_long ? (pe.
price
- adv_px) * pe.
qty
: (adv_px - pe.
price
) * pe.
qty
;
if
(favorable > pe.
max_runup
) pe.
max_runup
= favorable;
if
(adverse > pe.
max_drawdown
) pe.
max_drawdown
= adverse;
//
Also consider close — in the magnifier path high/low include the
//
running extremes but the final sampled price matters for mid-bar
//
exits that close the trade before the bar completes.
double
closing = is_long ? (cl - pe.
price
) * pe.
qty
: (pe.
price
- cl) * pe.
qty
;
if
(closing > pe.
max_runup
) pe.
max_runup
= closing;
double
closing_dd = -closing;
if
(closing_dd > pe.
max_drawdown
) pe.
max_drawdown
= closing_dd;
}
}
}
//
namespace pineforge
Back
|
FazBrowse Home
|
New Git URL