PineForge v0.13.1-379-g9b50973
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
order_action.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cmath>
5#include <optional>
6
8
9struct Reduce { double units; };
10struct Transact { double signed_units; };
11
12// A pure quantity transition. The plan has no identity, queue lifetime,
13// accounting, policy or validity promise; callers apply it synchronously to
14// the authoritative position after their own preconditions pass.
15class Plan {
16public:
17 double before() const noexcept { return before_; }
18 double after() const noexcept { return after_; }
19 double close_units() const noexcept { return close_units_; }
20 double open_units() const noexcept { return open_units_; }
21 bool no_effect() const noexcept { return before_ == after_; }
22
23private:
24 friend std::optional<Plan> plan(double, Reduce) noexcept;
25 friend std::optional<Plan> plan(double, Transact) noexcept;
26 Plan(double before, double after, double close_units, double open_units) noexcept
27 : before_(before), after_(after), close_units_(close_units),
28 open_units_(open_units) {}
29 const double before_;
30 const double after_;
31 const double close_units_;
32 const double open_units_;
33};
34
35inline bool finite(double value) noexcept { return std::isfinite(value); }
36
37inline std::optional<Plan> plan(double signed_position, Reduce request) noexcept {
38 if (!finite(signed_position) || !finite(request.units) || request.units < 0.0)
39 return std::nullopt;
40 const double exposure = std::abs(signed_position);
41 const double close = std::min(request.units, exposure);
42 const double signed_close = signed_position > 0.0 ? -close
43 : signed_position < 0.0 ? close : 0.0;
44 // The transition is deliberately one binary64 operation. A nonzero
45 // requested reduction that cannot change the representable position is
46 // refused instead of silently claiming execution.
47 const double after = signed_position + signed_close;
48 if (!finite(after) || !finite(close) || !finite(signed_close))
49 return std::nullopt;
50 if (request.units > 0.0 && signed_position != 0.0 && after == signed_position)
51 return std::nullopt;
52 return Plan(signed_position, after, close, 0.0);
53}
54
55inline std::optional<Plan> plan(double signed_position, Transact request) noexcept {
56 if (!finite(signed_position) || !finite(request.signed_units))
57 return std::nullopt;
58 // Compute the resulting position once, before decomposition. This is the
59 // authoritative native arithmetic operation; close/open are its audit
60 // decomposition and never substitute a second position calculation.
61 const double after = signed_position + request.signed_units;
62 if (!finite(after)) return std::nullopt;
63 if (request.signed_units != 0.0 && after == signed_position)
64 return std::nullopt; // nonzero request was absorbed by binary64
65
66 double close = 0.0;
67 double open = request.signed_units;
68 if (signed_position != 0.0 && request.signed_units != 0.0
69 && ((signed_position > 0.0) != (request.signed_units > 0.0))) {
70 close = std::min(std::abs(signed_position), std::abs(request.signed_units));
71 const double remainder = std::abs(request.signed_units) - close;
72 open = request.signed_units > 0.0 ? remainder : -remainder;
73 }
74 if (!finite(close) || close < 0.0 || !finite(open)) return std::nullopt;
75 return Plan(signed_position, after, close, open);
76}
77
78} // namespace pineforge::order_action
friend std::optional< Plan > plan(double, Reduce) noexcept
double close_units() const noexcept
double before() const noexcept
bool no_effect() const noexcept
double after() const noexcept
double open_units() const noexcept
bool finite(double value) noexcept
std::optional< Plan > plan(double signed_position, Reduce request) noexcept