Skip to main content

arvo_api/generated/
arvo.research.v1.rs

1// This file is @generated by prost-build.
2#[derive(serde::Serialize, serde::Deserialize)]
3#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
4pub struct Strategy {
5    #[prost(string, tag = "1")]
6    pub name: ::prost::alloc::string::String,
7    #[prost(string, tag = "2")]
8    pub label: ::prost::alloc::string::String,
9    /// The resolution it runs at, e.g. "1d" or "5m".
10    #[prost(string, tag = "3")]
11    pub interval: ::prost::alloc::string::String,
12    /// What the rule claims about how prices behave.
13    #[prost(string, tag = "4")]
14    pub premise: ::prost::alloc::string::String,
15    /// True when the rule ranks instruments against each other and needs a set.
16    #[prost(bool, tag = "5")]
17    pub ranks_a_set: bool,
18    /// Backtests one study runs: what a front end's progress indicator promises.
19    #[prost(uint32, tag = "6")]
20    pub backtests: u32,
21}
22#[derive(serde::Serialize, serde::Deserialize)]
23#[derive(Clone, PartialEq, ::prost::Message)]
24pub struct Strategies {
25    #[prost(message, repeated, tag = "1")]
26    pub strategies: ::prost::alloc::vec::Vec<Strategy>,
27}
28#[derive(serde::Serialize, serde::Deserialize)]
29#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
30pub struct FindingSummary {
31    #[prost(string, tag = "1")]
32    pub id: ::prost::alloc::string::String,
33    /// What was run, e.g. "study", "walk-forward" or "panel".
34    #[prost(string, tag = "2")]
35    pub kind: ::prost::alloc::string::String,
36    #[prost(string, tag = "3")]
37    pub subject: ::prost::alloc::string::String,
38    /// "Supported", "NotSupported" or "Inconclusive".
39    #[prost(string, tag = "4")]
40    pub verdict: ::prost::alloc::string::String,
41    /// RFC 3339.
42    #[prost(string, tag = "5")]
43    pub recorded_at: ::prost::alloc::string::String,
44    /// The agent or script that ran it; empty for a person.
45    #[prost(string, tag = "6")]
46    pub author: ::prost::alloc::string::String,
47    /// Which rule or ruleset ran, by the name the picker shows. A ruleset is
48    /// the file `rulesets/<name>.json` in the project.
49    #[prost(string, tag = "7")]
50    pub strategy: ::prost::alloc::string::String,
51    /// The engine build that produced it (#189): the git commit, with `-dirty`
52    /// when the tree had uncommitted changes, or `unknown` for a build made
53    /// outside a checkout.
54    #[prost(string, tag = "8")]
55    pub code_commit: ::prost::alloc::string::String,
56    /// The ruleset document's content hash at run time, when the strategy was
57    /// a ruleset rather than a shipped rule; empty otherwise. A finding whose
58    /// hash no longer matches the file is stale: the rule it measured is not
59    /// the rule the file now holds.
60    #[prost(string, tag = "9")]
61    pub ruleset_hash: ::prost::alloc::string::String,
62}
63#[derive(serde::Serialize, serde::Deserialize)]
64#[derive(Clone, PartialEq, ::prost::Message)]
65pub struct Findings {
66    #[prost(message, repeated, tag = "1")]
67    pub findings: ::prost::alloc::vec::Vec<FindingSummary>,
68    /// Records in the evidence store that could not be read.
69    #[prost(uint32, tag = "2")]
70    pub unreadable: u32,
71}
72#[derive(serde::Serialize, serde::Deserialize)]
73#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
74pub struct FindingId {
75    #[prost(string, tag = "1")]
76    pub id: ::prost::alloc::string::String,
77}
78#[derive(serde::Serialize, serde::Deserialize)]
79#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
80pub struct FindingIds {
81    #[prost(string, repeated, tag = "1")]
82    pub ids: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
83}
84#[derive(serde::Serialize, serde::Deserialize)]
85#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
86pub struct Advice {
87    /// "blocking", "warning" or "note".
88    #[prost(string, tag = "1")]
89    pub severity: ::prost::alloc::string::String,
90    #[prost(string, tag = "2")]
91    pub finding: ::prost::alloc::string::String,
92    #[prost(string, tag = "3")]
93    pub action: ::prost::alloc::string::String,
94    #[prost(string, tag = "4")]
95    pub evidence: ::prost::alloc::string::String,
96}
97/// One finding: the verdict and how far it may be read, before any number.
98#[derive(serde::Serialize, serde::Deserialize)]
99#[derive(Clone, PartialEq, ::prost::Message)]
100pub struct Finding {
101    #[prost(message, optional, tag = "1")]
102    pub summary: ::core::option::Option<FindingSummary>,
103    /// A plain instruction on how far the verdict may be read. Read first.
104    #[prost(string, tag = "2")]
105    pub read_this_first: ::prost::alloc::string::String,
106    #[prost(string, repeated, tag = "3")]
107    pub reasons: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
108    #[prost(message, repeated, tag = "4")]
109    pub advice: ::prost::alloc::vec::Vec<Advice>,
110    /// The numbers, which differ by kind: the search and the out-of-sample
111    /// metrics for a study, the stitched record for a walk-forward, the pooled
112    /// outcome for a panel. JSON, so a new number is not a new message.
113    #[prost(string, tag = "5")]
114    pub detail_json: ::prost::alloc::string::String,
115    /// Files kept with the finding (#157).
116    #[prost(message, repeated, tag = "6")]
117    pub attachments: ::prost::alloc::vec::Vec<Attachment>,
118}
119/// One file kept with a finding: bytes stored once by content hash, listed
120/// on the record.
121#[derive(serde::Serialize, serde::Deserialize)]
122#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
123pub struct Attachment {
124    #[prost(string, tag = "1")]
125    pub name: ::prost::alloc::string::String,
126    #[prost(string, tag = "2")]
127    pub media_type: ::prost::alloc::string::String,
128    /// blake3 of the bytes, hex.
129    #[prost(string, tag = "3")]
130    pub hash: ::prost::alloc::string::String,
131    #[prost(uint64, tag = "4")]
132    pub bytes: u64,
133    /// RFC 3339.
134    #[prost(string, tag = "5")]
135    pub added_at: ::prost::alloc::string::String,
136}
137/// A file to keep with a finding the caller already recorded or ran.
138#[derive(serde::Serialize, serde::Deserialize)]
139#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
140pub struct AttachRequest {
141    #[prost(string, tag = "1")]
142    pub finding_id: ::prost::alloc::string::String,
143    /// As the author names it: `report.md`, `equity.png`.
144    #[prost(string, tag = "2")]
145    pub name: ::prost::alloc::string::String,
146    /// `text/markdown`, `image/png`; empty means application/octet-stream.
147    #[prost(string, tag = "3")]
148    pub media_type: ::prost::alloc::string::String,
149    #[prost(bytes = "vec", tag = "4")]
150    pub data: ::prost::alloc::vec::Vec<u8>,
151}
152#[derive(serde::Serialize, serde::Deserialize)]
153#[derive(Clone, PartialEq, ::prost::Message)]
154pub struct Attachments {
155    #[prost(message, repeated, tag = "1")]
156    pub attachments: ::prost::alloc::vec::Vec<Attachment>,
157}
158#[derive(serde::Serialize, serde::Deserialize)]
159#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
160pub struct AttachmentRef {
161    #[prost(string, tag = "1")]
162    pub finding_id: ::prost::alloc::string::String,
163    #[prost(string, tag = "2")]
164    pub hash: ::prost::alloc::string::String,
165}
166#[derive(serde::Serialize, serde::Deserialize)]
167#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
168pub struct RunRequest {
169    #[prost(string, tag = "1")]
170    pub instrument: ::prost::alloc::string::String,
171    #[prost(string, tag = "2")]
172    pub strategy: ::prost::alloc::string::String,
173    /// Who is running this, e.g. "script:momentum-scan". Required: a run is
174    /// saved as this author's and deflated against everything the author has
175    /// run, so a script iterating until something passes does not make it pass.
176    #[prost(string, tag = "3")]
177    pub author: ::prost::alloc::string::String,
178    /// Where in the author's code the call was made, as "path:line", so the
179    /// workbench can put the finding's caveats against that line. Optional.
180    #[prost(string, tag = "4")]
181    pub origin: ::prost::alloc::string::String,
182}
183/// Bars, or the regimes read off them, over a window of the library (#195).
184#[derive(serde::Serialize, serde::Deserialize)]
185#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
186pub struct BarsRequest {
187    #[prost(string, tag = "1")]
188    pub instrument: ::prost::alloc::string::String,
189    /// "1day" when absent; "5minute" and the other minute steps where the
190    /// library holds them.
191    #[prost(string, optional, tag = "2")]
192    pub interval: ::core::option::Option<::prost::alloc::string::String>,
193    /// YYYY-MM-DD, inclusive. The library's whole range when absent.
194    #[prost(string, optional, tag = "3")]
195    pub from: ::core::option::Option<::prost::alloc::string::String>,
196    #[prost(string, optional, tag = "4")]
197    pub to: ::core::option::Option<::prost::alloc::string::String>,
198    /// At most this many bars, from the end of the window. 60 when absent;
199    /// never more than 2000.
200    #[prost(uint32, optional, tag = "5")]
201    pub last: ::core::option::Option<u32>,
202}
203/// The leaderboard's filter (#226). Empty is every finding.
204#[derive(serde::Serialize, serde::Deserialize)]
205#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
206pub struct RankRequest {
207    /// The rule's name, as the finding recorded it.
208    #[prost(string, optional, tag = "1")]
209    pub rule: ::core::option::Option<::prost::alloc::string::String>,
210    /// SYMBOL.VENUE.
211    #[prost(string, optional, tag = "2")]
212    pub instrument: ::core::option::Option<::prost::alloc::string::String>,
213}
214/// The review after the close (#217), by day.
215#[derive(serde::Serialize, serde::Deserialize)]
216#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
217pub struct ReviewRequest {
218    /// YYYY-MM-DD, UTC. Today when absent.
219    #[prost(string, optional, tag = "1")]
220    pub day: ::core::option::Option<::prost::alloc::string::String>,
221}
222#[derive(serde::Serialize, serde::Deserialize)]
223#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
224pub struct StudyRequest {
225    #[prost(string, tag = "1")]
226    pub instrument: ::prost::alloc::string::String,
227    /// A rule or ruleset name; the default rule when absent.
228    #[prost(string, optional, tag = "2")]
229    pub strategy: ::core::option::Option<::prost::alloc::string::String>,
230}
231#[derive(serde::Serialize, serde::Deserialize)]
232#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
233pub struct BookRequest {
234    #[prost(string, repeated, tag = "1")]
235    pub instruments: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
236    #[prost(string, optional, tag = "2")]
237    pub strategy: ::core::option::Option<::prost::alloc::string::String>,
238    #[prost(uint32, optional, tag = "3")]
239    pub max_concurrent_positions: ::core::option::Option<u32>,
240    #[prost(uint32, optional, tag = "4")]
241    pub max_per_sector: ::core::option::Option<u32>,
242}
243/// A panel over a universe (#227): a file under the project's universes/.
244#[derive(serde::Serialize, serde::Deserialize)]
245#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
246pub struct PanelRequest {
247    #[prost(string, tag = "1")]
248    pub universe: ::prost::alloc::string::String,
249    /// The default rule when absent.
250    #[prost(string, optional, tag = "2")]
251    pub strategy: ::core::option::Option<::prost::alloc::string::String>,
252}
253#[derive(serde::Serialize, serde::Deserialize)]
254#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
255pub struct SharedExperiment {
256    /// The file's text, as the person pasted or opened it.
257    #[prost(string, tag = "1")]
258    pub text: ::prost::alloc::string::String,
259    #[prost(string, tag = "2")]
260    pub instrument: ::prost::alloc::string::String,
261}
262/// One point of an equity curve. `at` is `YYYY-MM-DDTHH:MM:SS` or a date.
263#[derive(serde::Serialize, serde::Deserialize)]
264#[derive(Clone, PartialEq, ::prost::Message)]
265pub struct Point {
266    #[prost(string, tag = "1")]
267    pub at: ::prost::alloc::string::String,
268    #[prost(double, tag = "2")]
269    pub equity: f64,
270}
271/// One trade in a ledger: a round trip, or a position still open at the end
272/// (empty `closed`, no `exit`).
273#[derive(serde::Serialize, serde::Deserialize)]
274#[derive(Clone, PartialEq, ::prost::Message)]
275pub struct LedgerTrade {
276    /// `SYMBOL.VENUE`; empty when it is the experiment's own instrument.
277    #[prost(string, tag = "1")]
278    pub instrument: ::prost::alloc::string::String,
279    #[prost(string, tag = "2")]
280    pub opened: ::prost::alloc::string::String,
281    #[prost(string, tag = "3")]
282    pub closed: ::prost::alloc::string::String,
283    /// "long" or "short"; empty means long.
284    #[prost(string, tag = "4")]
285    pub direction: ::prost::alloc::string::String,
286    #[prost(double, tag = "5")]
287    pub quantity: f64,
288    #[prost(double, tag = "6")]
289    pub entry: f64,
290    #[prost(double, optional, tag = "7")]
291    pub exit: ::core::option::Option<f64>,
292    #[prost(double, tag = "8")]
293    pub pnl: f64,
294    #[prost(double, tag = "9")]
295    pub commission: f64,
296    /// "signal", "stop", "halted", "expired" or "still_open"; empty means signal.
297    #[prost(string, tag = "10")]
298    pub exit_reason: ::prost::alloc::string::String,
299    /// What the rule saw at entry (#190): the condition that fired, the value it
300    /// was judged on, the regime it saw, and the quantity it asked the gate for.
301    /// Absent on a trade recorded before the journal or opened by no rule.
302    #[prost(string, optional, tag = "11")]
303    pub rule: ::core::option::Option<::prost::alloc::string::String>,
304    #[prost(double, optional, tag = "12")]
305    pub signal: ::core::option::Option<f64>,
306    #[prost(string, optional, tag = "13")]
307    pub regime: ::core::option::Option<::prost::alloc::string::String>,
308    #[prost(double, optional, tag = "14")]
309    pub asked: ::core::option::Option<f64>,
310}
311/// Evidence an engine Arvo did not run computed (ADR-0026). What a run
312/// produces, before anyone concludes anything from it; Arvo computes the
313/// verdict. There is no field for a verdict, a metric or a p-value.
314#[derive(serde::Serialize, serde::Deserialize)]
315#[derive(Clone, PartialEq, ::prost::Message)]
316pub struct ReportRequest {
317    /// As RunRequest: who is recording this, and where in their code.
318    #[prost(string, tag = "1")]
319    pub author: ::prost::alloc::string::String,
320    #[prost(string, tag = "2")]
321    pub origin: ::prost::alloc::string::String,
322    /// The hypothesis: an id, and the claim in prose.
323    #[prost(string, tag = "3")]
324    pub hypothesis_id: ::prost::alloc::string::String,
325    #[prost(string, tag = "4")]
326    pub claim: ::prost::alloc::string::String,
327    /// The experiment as data.
328    #[prost(string, tag = "5")]
329    pub instrument: ::prost::alloc::string::String,
330    /// ISO dates, inclusive.
331    #[prost(string, tag = "6")]
332    pub from: ::prost::alloc::string::String,
333    #[prost(string, tag = "7")]
334    pub to: ::prost::alloc::string::String,
335    /// As arvo_data spells it: "1day", "5minute".
336    #[prost(string, tag = "8")]
337    pub interval: ::prost::alloc::string::String,
338    /// The engine's own dataset: an id, and a content hash of what it read.
339    #[prost(string, tag = "9")]
340    pub dataset_id: ::prost::alloc::string::String,
341    #[prost(string, tag = "10")]
342    pub dataset_version: ::prost::alloc::string::String,
343    /// "split" (the default) or "total_return".
344    #[prost(string, tag = "11")]
345    pub adjustment: ::prost::alloc::string::String,
346    /// The engine's own strategy name, and its parameters.
347    #[prost(string, tag = "12")]
348    pub strategy: ::prost::alloc::string::String,
349    #[prost(map = "string, double", tag = "13")]
350    pub params: ::std::collections::HashMap<::prost::alloc::string::String, f64>,
351    /// The cost model the run assumed.
352    #[prost(double, tag = "14")]
353    pub commission_bps: f64,
354    #[prost(double, tag = "15")]
355    pub slippage_bps: f64,
356    #[prost(double, tag = "16")]
357    pub per_fill: f64,
358    /// The risk model, as far as the run assumed one.
359    #[prost(double, optional, tag = "17")]
360    pub stop_atr_multiple: ::core::option::Option<f64>,
361    /// Zero means the curve's first point.
362    #[prost(double, tag = "18")]
363    pub starting_cash: f64,
364    /// What computed this, by name and version.
365    #[prost(string, tag = "19")]
366    pub engine: ::prost::alloc::string::String,
367    /// The evidence.
368    #[prost(message, repeated, tag = "20")]
369    pub strategy_curve: ::prost::alloc::vec::Vec<Point>,
370    #[prost(message, repeated, tag = "21")]
371    pub ledger: ::prost::alloc::vec::Vec<LedgerTrade>,
372    /// Buy-and-hold over the same window and capital. Without it the finding
373    /// is Inconclusive: a return with nothing to beat is not a result.
374    #[prost(message, repeated, tag = "22")]
375    pub benchmark_curve: ::prost::alloc::vec::Vec<Point>,
376    /// How many trials the author's whole search has run (ADR-0014).
377    #[prost(uint32, optional, tag = "23")]
378    pub trials: ::core::option::Option<u32>,
379}
380#[derive(serde::Serialize, serde::Deserialize)]
381#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
382pub struct ReportFigure {
383    #[prost(string, tag = "1")]
384    pub finding_id: ::prost::alloc::string::String,
385    /// The equity chart the front end captured, base64 PNG. Absent is a report
386    /// with no figure, not an error: only a window can draw one.
387    #[prost(string, optional, tag = "2")]
388    pub figure: ::core::option::Option<::prost::alloc::string::String>,
389}
390#[derive(serde::Serialize, serde::Deserialize)]
391#[derive(Clone, PartialEq, ::prost::Message)]
392pub struct TradeExport {
393    #[prost(string, tag = "1")]
394    pub name: ::prost::alloc::string::String,
395    /// The rows as the front end has them, in the order it is showing them. The
396    /// engine does not derive these from the finding, because the sort order is
397    /// the view's, and an export that silently differed from the table above it
398    /// would be worse than none.
399    #[prost(message, repeated, tag = "2")]
400    pub rows: ::prost::alloc::vec::Vec<TradeRow>,
401}
402/// One round trip, as a trades table shows it and a CSV export writes it.
403#[derive(serde::Serialize, serde::Deserialize)]
404#[derive(Clone, PartialEq, ::prost::Message)]
405pub struct TradeRow {
406    /// Which instrument the round trip was in. Empty on an ordinary study,
407    /// whose subject line already says; on a book it is the difference between
408    /// a usable export and trades from three instruments with no way to tell
409    /// them apart.
410    #[prost(string, tag = "1")]
411    pub instrument: ::prost::alloc::string::String,
412    #[prost(string, tag = "2")]
413    pub opened: ::prost::alloc::string::String,
414    #[prost(string, tag = "3")]
415    pub closed: ::prost::alloc::string::String,
416    #[prost(string, tag = "4")]
417    pub direction: ::prost::alloc::string::String,
418    #[prost(double, tag = "5")]
419    pub quantity: f64,
420    #[prost(double, tag = "6")]
421    pub entry: f64,
422    #[prost(double, optional, tag = "7")]
423    pub exit: ::core::option::Option<f64>,
424    #[prost(double, tag = "8")]
425    pub pnl: f64,
426    #[prost(double, tag = "9")]
427    pub commission: f64,
428    #[prost(double, optional, tag = "10")]
429    pub held_days: ::core::option::Option<f64>,
430    #[prost(string, tag = "11")]
431    pub exit_reason: ::prost::alloc::string::String,
432    /// What the rule saw at entry (#190): the condition that fired, the value it
433    /// was judged on, the regime it saw, and the quantity it asked the gate for.
434    /// Absent on a trade recorded before the journal or opened by no rule.
435    #[prost(string, optional, tag = "12")]
436    pub rule: ::core::option::Option<::prost::alloc::string::String>,
437    #[prost(double, optional, tag = "13")]
438    pub signal: ::core::option::Option<f64>,
439    #[prost(string, optional, tag = "14")]
440    pub regime: ::core::option::Option<::prost::alloc::string::String>,
441    #[prost(double, optional, tag = "15")]
442    pub asked: ::core::option::Option<f64>,
443}
444/// A ruleset parameter: one value fixes it, several search it.
445#[derive(serde::Serialize, serde::Deserialize)]
446#[derive(Clone, PartialEq, ::prost::Message)]
447pub struct Param {
448    #[prost(string, tag = "1")]
449    pub name: ::prost::alloc::string::String,
450    #[prost(double, repeated, tag = "2")]
451    pub values: ::prost::alloc::vec::Vec<f64>,
452}
453#[derive(serde::Serialize, serde::Deserialize)]
454#[derive(Clone, PartialEq, ::prost::Message)]
455pub struct Fixed {
456    #[prost(string, tag = "1")]
457    pub name: ::prost::alloc::string::String,
458    #[prost(double, tag = "2")]
459    pub value: f64,
460}
461/// One file under `<project>`/rulesets/, as the picker sees it.
462#[derive(serde::Serialize, serde::Deserialize)]
463#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
464pub struct Ruleset {
465    #[prost(string, tag = "1")]
466    pub path: ::prost::alloc::string::String,
467    #[prost(string, tag = "2")]
468    pub name: ::prost::alloc::string::String,
469    #[prost(string, tag = "3")]
470    pub label: ::prost::alloc::string::String,
471    #[prost(string, tag = "4")]
472    pub rule: ::prost::alloc::string::String,
473    #[prost(string, tag = "5")]
474    pub interval: ::prost::alloc::string::String,
475    #[prost(uint32, tag = "6")]
476    pub searches: u32,
477    /// Why it cannot be run, when it cannot.
478    #[prost(string, optional, tag = "7")]
479    pub problem: ::core::option::Option<::prost::alloc::string::String>,
480}
481#[derive(serde::Serialize, serde::Deserialize)]
482#[derive(Clone, PartialEq, ::prost::Message)]
483pub struct Rulesets {
484    #[prost(message, repeated, tag = "1")]
485    pub rulesets: ::prost::alloc::vec::Vec<Ruleset>,
486}
487#[derive(serde::Serialize, serde::Deserialize)]
488#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
489pub struct RulesetPath {
490    #[prost(string, tag = "1")]
491    pub path: ::prost::alloc::string::String,
492}
493/// A grid ruleset's parts, for a form.
494#[derive(serde::Serialize, serde::Deserialize)]
495#[derive(Clone, PartialEq, ::prost::Message)]
496pub struct RulesetForm {
497    #[prost(string, tag = "1")]
498    pub name: ::prost::alloc::string::String,
499    #[prost(string, tag = "2")]
500    pub rule: ::prost::alloc::string::String,
501    #[prost(string, tag = "3")]
502    pub label: ::prost::alloc::string::String,
503    #[prost(string, tag = "4")]
504    pub premise: ::prost::alloc::string::String,
505    #[prost(message, repeated, tag = "5")]
506    pub params: ::prost::alloc::vec::Vec<Param>,
507}
508/// A rule this build implements, with the search it ships.
509#[derive(serde::Serialize, serde::Deserialize)]
510#[derive(Clone, PartialEq, ::prost::Message)]
511pub struct Rule {
512    #[prost(string, tag = "1")]
513    pub name: ::prost::alloc::string::String,
514    #[prost(string, tag = "2")]
515    pub label: ::prost::alloc::string::String,
516    #[prost(string, tag = "3")]
517    pub premise: ::prost::alloc::string::String,
518    #[prost(string, tag = "4")]
519    pub interval: ::prost::alloc::string::String,
520    #[prost(message, repeated, tag = "5")]
521    pub fixed: ::prost::alloc::vec::Vec<Fixed>,
522    #[prost(message, repeated, tag = "6")]
523    pub axes: ::prost::alloc::vec::Vec<Param>,
524    #[prost(bool, tag = "7")]
525    pub ranks_a_set: bool,
526    #[prost(bool, tag = "8")]
527    pub trades_options: bool,
528}
529#[derive(serde::Serialize, serde::Deserialize)]
530#[derive(Clone, PartialEq, ::prost::Message)]
531pub struct Rules {
532    #[prost(message, repeated, tag = "1")]
533    pub rules: ::prost::alloc::vec::Vec<Rule>,
534}
535/// The project's risk file (.arvo/risk.json), as read for the next study.
536#[derive(serde::Serialize, serde::Deserialize)]
537#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
538pub struct RiskModel {
539    #[prost(string, tag = "1")]
540    pub path: ::prost::alloc::string::String,
541    #[prost(bool, tag = "2")]
542    pub exists: bool,
543    /// The model in force, as JSON: the file's when it reads, the shipped one
544    /// when it does not.
545    #[prost(string, tag = "3")]
546    pub model_json: ::prost::alloc::string::String,
547    /// Why the file was refused, when it was.
548    #[prost(string, optional, tag = "4")]
549    pub error: ::core::option::Option<::prost::alloc::string::String>,
550}
551/// The dividend bias, measured.
552///
553/// On split-adjusted prices no dividend is paid to anything in a backtest.
554/// Both sides forgo them — but the benchmark held through every ex-date and a
555/// rule in the market some of the time did not, so the margin between them is
556/// overstated in the strategy's favour, and \[`Self::corrected_excess`\] takes it
557/// off. On total-return prices the distributions are already in the returns and
558/// the same measurement describes what the margin is made of instead.
559/// A study's result after tax. See `arvo_research::tax` for what is modelled.
560#[derive(serde::Serialize, serde::Deserialize)]
561#[derive(Clone, Copy, PartialEq, ::prost::Message)]
562pub struct AfterTaxView {
563    #[prost(double, tag = "1")]
564    pub strategy_return: f64,
565    #[prost(double, tag = "2")]
566    pub benchmark_return: f64,
567    /// `strategy_return - benchmark_return`: the margin a taxable account
568    /// would actually have kept.
569    #[prost(double, tag = "3")]
570    pub excess: f64,
571    #[prost(double, tag = "4")]
572    pub strategy_tax: f64,
573    #[prost(double, tag = "5")]
574    pub benchmark_tax: f64,
575    #[prost(double, tag = "6")]
576    pub short_term_rate: f64,
577    #[prost(double, tag = "7")]
578    pub long_term_rate: f64,
579}
580/// What one account holding every member of a panel would have done.
581///
582/// The panel's pooled figures average its members. This combines them, which
583/// is a different number in the place that matters: falls that did not
584/// coincide hurt a book less than they hurt its average member.
585#[derive(serde::Serialize, serde::Deserialize)]
586#[derive(Clone, Copy, PartialEq, ::prost::Message)]
587pub struct BookView {
588    #[prost(message, optional, tag = "1")]
589    pub metrics: ::core::option::Option<MetricsView>,
590    /// Mean member drawdown less the book's. Positive is diversification;
591    /// zero or below means the members fell together and the panel was one bet.
592    #[prost(double, tag = "2")]
593    pub diversification: f64,
594}
595/// How much of a panel's apparent breadth is real.
596///
597/// The pooled statistics read as evidence in proportion to the instrument
598/// count — three that agree feel like three times the confidence of one. They
599/// are not, if the three moved together.
600#[derive(serde::Serialize, serde::Deserialize)]
601#[derive(Clone, PartialEq, ::prost::Message)]
602pub struct BreadthView {
603    #[prost(string, repeated, tag = "1")]
604    pub instruments: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
605    /// Square and symmetric. `None` where a pair had too little overlap to
606    /// say — which is different from a correlation of zero.
607    #[prost(message, repeated, tag = "2")]
608    pub correlations: ::prost::alloc::vec::Vec<CorrelationRow>,
609    #[prost(double, optional, tag = "3")]
610    pub mean_correlation: ::core::option::Option<f64>,
611    /// How many independent instruments the panel behaves like.
612    #[prost(double, optional, tag = "4")]
613    pub effective: ::core::option::Option<f64>,
614    /// How much the pooled average's certainty is overstated by counting
615    /// instruments instead of independent ones.
616    #[prost(double, optional, tag = "5")]
617    pub overstatement: ::core::option::Option<f64>,
618}
619/// One finding in a side-by-side comparison.
620#[derive(serde::Serialize, serde::Deserialize)]
621#[derive(Clone, PartialEq, ::prost::Message)]
622pub struct ComparisonRowView {
623    #[prost(string, tag = "1")]
624    pub id: ::prost::alloc::string::String,
625    #[prost(string, tag = "2")]
626    pub subject: ::prost::alloc::string::String,
627    #[prost(string, tag = "3")]
628    pub kind: ::prost::alloc::string::String,
629    #[prost(string, tag = "4")]
630    pub strategy_name: ::prost::alloc::string::String,
631    #[prost(string, tag = "5")]
632    pub verdict: ::prost::alloc::string::String,
633    #[prost(string, tag = "6")]
634    pub recorded_at: ::prost::alloc::string::String,
635    #[prost(double, tag = "7")]
636    pub total_return: f64,
637    #[prost(double, tag = "8")]
638    pub excess_return: f64,
639    #[prost(double, optional, tag = "9")]
640    pub sharpe: ::core::option::Option<f64>,
641    #[prost(double, tag = "10")]
642    pub max_drawdown: f64,
643    #[prost(uint32, tag = "11")]
644    pub trades: u32,
645    #[prost(double, optional, tag = "12")]
646    pub win_rate: ::core::option::Option<f64>,
647    #[prost(double, optional, tag = "13")]
648    pub profit_factor: ::core::option::Option<f64>,
649    /// Whether the data behind it has changed since it was recorded.
650    #[prost(bool, optional, tag = "14")]
651    pub stale: ::core::option::Option<bool>,
652}
653/// Several findings, read against each other.
654#[derive(serde::Serialize, serde::Deserialize)]
655#[derive(Clone, PartialEq, ::prost::Message)]
656pub struct ComparisonView {
657    #[prost(message, repeated, tag = "1")]
658    pub rows: ::prost::alloc::vec::Vec<ComparisonRowView>,
659    #[prost(message, repeated, tag = "2")]
660    pub curves: ::prost::alloc::vec::Vec<NamedCurveView>,
661    /// The best out-of-sample Sharpe among them, and the score the best of a
662    /// comparison this size would be expected to reach with no skill at all.
663    ///
664    /// The point of the whole screen. Comparing six strategies and keeping the
665    /// best one is a search of size six, and until now nothing counted it:
666    /// each finding deflated the grid *inside* it and none of them knew about
667    /// the other five.
668    #[prost(double, optional, tag = "3")]
669    pub best_sharpe: ::core::option::Option<f64>,
670    #[prost(double, optional, tag = "4")]
671    pub expected_best_under_null: ::core::option::Option<f64>,
672    #[prost(bool, tag = "5")]
673    pub survived_deflation: bool,
674    /// What is worth saying about the comparison itself.
675    #[prost(string, repeated, tag = "6")]
676    pub notes: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
677}
678/// One row of a correlation matrix. A cell with no value is a pair with too
679/// little overlap to say anything about, which is not the same as zero.
680#[derive(serde::Serialize, serde::Deserialize)]
681#[derive(Clone, PartialEq, ::prost::Message)]
682pub struct CorrelationRow {
683    #[prost(message, repeated, tag = "1")]
684    pub cells: ::prost::alloc::vec::Vec<OptionalDouble>,
685}
686/// One point on a curve, in the shape a chart library wants: an ISO date and
687/// a value.
688#[derive(serde::Serialize, serde::Deserialize)]
689#[derive(Clone, Copy, PartialEq, ::prost::Message)]
690pub struct CurvePoint {
691    /// Seconds since the epoch, not a date string.
692    ///
693    /// A date was enough while every curve was daily and is not once two
694    /// points can share a day: the chart keys points by time, so an intraday
695    /// series collapsed to dates loses every point but the last of each day.
696    /// The previous version did exactly that, deliberately and with a comment
697    /// saying so — this is what removing the limitation instead looks like.
698    #[prost(int64, tag = "1")]
699    pub time: i64,
700    #[prost(double, tag = "2")]
701    pub value: f64,
702}
703/// Where a replay first stopped matching the record.
704#[derive(serde::Serialize, serde::Deserialize)]
705#[derive(Clone, PartialEq, ::prost::Message)]
706pub struct DivergenceView {
707    #[prost(string, tag = "1")]
708    pub what: ::prost::alloc::string::String,
709    #[prost(uint32, optional, tag = "2")]
710    pub at: ::core::option::Option<u32>,
711    #[prost(string, optional, tag = "3")]
712    pub when: ::core::option::Option<::prost::alloc::string::String>,
713    #[prost(double, tag = "4")]
714    pub recorded: f64,
715    #[prost(double, tag = "5")]
716    pub replayed: f64,
717    /// Gap relative to the recorded value, so a report can separate a
718    /// rounding difference from a different answer.
719    #[prost(double, tag = "6")]
720    pub relative: f64,
721}
722#[derive(serde::Serialize, serde::Deserialize)]
723#[derive(Clone, Copy, PartialEq, ::prost::Message)]
724pub struct DividendGapView {
725    /// Distributions that went ex inside the window.
726    ///
727    /// Zero is a real answer and a good one: these instruments pay nothing, so
728    /// the margin needs no correction.
729    #[prost(uint32, tag = "1")]
730    pub events: u32,
731    #[prost(double, tag = "2")]
732    pub strategy_income: f64,
733    #[prost(double, tag = "3")]
734    pub benchmark_income: f64,
735    /// The gap as a fraction of starting capital, in the same units as the
736    /// excess return it comes off.
737    #[prost(double, tag = "4")]
738    pub overstatement: f64,
739    /// The excess return with the gap taken out, where taking it out means
740    /// anything.
741    ///
742    /// `None` on a total-return dataset: the distributions are already in the
743    /// excess return there, so subtracting the gap again would report a margin
744    /// smaller than the account earned. The rest of this view still holds —
745    /// only the subtraction stops being valid.
746    #[prost(double, optional, tag = "5")]
747    pub corrected_excess: ::core::option::Option<f64>,
748    /// False when some instrument the run held has no distribution series, in
749    /// which case the figure is a floor rather than the answer.
750    #[prost(bool, tag = "6")]
751    pub complete: bool,
752    #[prost(uint32, tag = "7")]
753    pub covered: u32,
754    #[prost(uint32, tag = "8")]
755    pub instruments: u32,
756}
757/// One fold of a walk-forward, flattened for display.
758#[derive(serde::Serialize, serde::Deserialize)]
759#[derive(Clone, PartialEq, ::prost::Message)]
760pub struct FoldView {
761    #[prost(string, tag = "1")]
762    pub chose_on: ::prost::alloc::string::String,
763    #[prost(string, tag = "2")]
764    pub judged_on: ::prost::alloc::string::String,
765    #[prost(message, repeated, tag = "3")]
766    pub params: ::prost::alloc::vec::Vec<NamedNumber>,
767    #[prost(double, tag = "4")]
768    pub strategy_return: f64,
769    #[prost(double, tag = "5")]
770    pub benchmark_return: f64,
771    #[prost(uint32, tag = "6")]
772    pub trades: u32,
773    /// Whether this fold's winner beat what a no-skill search of that size
774    /// would produce. Per fold, because a procedure that selects noise in most
775    /// periods has not been shown to select.
776    #[prost(bool, tag = "7")]
777    pub survived_deflation: bool,
778}
779#[derive(serde::Serialize, serde::Deserialize)]
780#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
781pub struct HistoryEntryView {
782    #[prost(string, tag = "1")]
783    pub id: ::prost::alloc::string::String,
784    #[prost(string, tag = "2")]
785    pub kind: ::prost::alloc::string::String,
786    #[prost(string, tag = "3")]
787    pub subject: ::prost::alloc::string::String,
788    #[prost(string, tag = "4")]
789    pub verdict: ::prost::alloc::string::String,
790    #[prost(string, tag = "5")]
791    pub recorded_at: ::prost::alloc::string::String,
792    /// True when the data this was produced from no longer matches disk, or
793    /// the ruleset it ran has changed since. `None` when the data it
794    /// referenced can no longer be found at all.
795    #[prost(bool, optional, tag = "6")]
796    pub stale: ::core::option::Option<bool>,
797    /// The agent that ran it, or `None` for a person. `default` for a view
798    /// built before agents could run research.
799    #[prost(string, optional, tag = "7")]
800    pub agent: ::core::option::Option<::prost::alloc::string::String>,
801    /// Where the agent's code asked for it, as `path:line`, when it said.
802    #[prost(string, optional, tag = "8")]
803    pub origin: ::core::option::Option<::prost::alloc::string::String>,
804    /// The size of the search an agent's finding was held to.
805    #[prost(uint32, optional, tag = "9")]
806    pub trials: ::core::option::Option<u32>,
807    /// How many files are kept with it (#157).
808    #[prost(uint32, tag = "10")]
809    pub attachments: u32,
810    /// What produced it (#189): the engine build, and for a ruleset the
811    /// document's hash at run time. Two findings from different versions of
812    /// one ruleset are told apart by this.
813    #[prost(string, tag = "11")]
814    pub code_commit: ::prost::alloc::string::String,
815    #[prost(string, tag = "12")]
816    pub ruleset_hash: ::prost::alloc::string::String,
817    /// Why `stale` is set: `data`, `ruleset`, or `data and ruleset`. Empty
818    /// when fresh, and when the data is gone altogether.
819    #[prost(string, tag = "13")]
820    pub stale_reason: ::prost::alloc::string::String,
821}
822/// What the workbench shows before anything has been run.
823/// Everything in research memory, and what could not be read of it.
824#[derive(serde::Serialize, serde::Deserialize)]
825#[derive(Clone, PartialEq, ::prost::Message)]
826pub struct HistoryView {
827    #[prost(message, repeated, tag = "1")]
828    pub entries: ::prost::alloc::vec::Vec<HistoryEntryView>,
829    #[prost(message, repeated, tag = "2")]
830    pub unreadable: ::prost::alloc::vec::Vec<UnreadableView>,
831}
832/// What one member of a book actually contributed.
833///
834/// A book reports one return for several instruments, and the single most
835/// useful question about it is which of them produced that return. The panel
836/// answers the same question by running members separately; a book cannot,
837/// because the members interfere — so it has to be read out of the ledger.
838#[derive(serde::Serialize, serde::Deserialize)]
839#[derive(Clone, PartialEq, ::prost::Message)]
840pub struct MemberView {
841    #[prost(string, tag = "1")]
842    pub instrument: ::prost::alloc::string::String,
843    #[prost(uint32, tag = "2")]
844    pub trades: u32,
845    /// Realised profit, net of what the venue charged.
846    #[prost(double, tag = "3")]
847    pub pnl: f64,
848    /// This member's share of the book's total realised profit.
849    ///
850    /// `None` when the book realised nothing at all, rather than a share of
851    /// zero — which would read as a measurement.
852    #[prost(double, optional, tag = "4")]
853    pub share: ::core::option::Option<f64>,
854    /// True when the member never opened a position.
855    ///
856    /// The failure mode a shared account introduces: an instrument that was
857    /// asked for, funded by nothing, and silently absent from every number.
858    #[prost(bool, tag = "5")]
859    pub silent: bool,
860}
861#[derive(serde::Serialize, serde::Deserialize)]
862#[derive(Clone, Copy, PartialEq, ::prost::Message)]
863pub struct MetricsView {
864    #[prost(double, tag = "1")]
865    pub total_return: f64,
866    #[prost(double, tag = "2")]
867    pub cagr: f64,
868    #[prost(double, tag = "3")]
869    pub max_drawdown: f64,
870    #[prost(double, tag = "4")]
871    pub volatility: f64,
872    #[prost(double, optional, tag = "5")]
873    pub sharpe: ::core::option::Option<f64>,
874    #[prost(double, optional, tag = "6")]
875    pub sortino: ::core::option::Option<f64>,
876    #[prost(double, optional, tag = "7")]
877    pub calmar: ::core::option::Option<f64>,
878    /// Probability the true Sharpe is above zero, given the sample.
879    ///
880    /// The second number a Sharpe needs. 1.2 from forty returns and 1.2 from
881    /// four thousand print identically and are not the same finding.
882    #[prost(double, optional, tag = "8")]
883    pub psr: ::core::option::Option<f64>,
884    #[prost(uint32, tag = "9")]
885    pub trades: u32,
886}
887/// One month of the strategy's return, for the heatmap.
888#[derive(serde::Serialize, serde::Deserialize)]
889#[derive(Clone, Copy, PartialEq, ::prost::Message)]
890pub struct MonthlyReturnView {
891    #[prost(int32, tag = "1")]
892    pub year: i32,
893    #[prost(uint32, tag = "2")]
894    pub month: u32,
895    #[prost(double, tag = "3")]
896    pub value: f64,
897}
898/// A named equity curve, for drawing several on one chart.
899#[derive(serde::Serialize, serde::Deserialize)]
900#[derive(Clone, PartialEq, ::prost::Message)]
901pub struct NamedCurveView {
902    #[prost(string, tag = "1")]
903    pub name: ::prost::alloc::string::String,
904    #[prost(message, repeated, tag = "2")]
905    pub points: ::prost::alloc::vec::Vec<CurvePoint>,
906}
907/// What a front end renders.
908///
909/// These are the shapes the engine answers with. They are here rather than
910/// in a Rust crate both sides link so that the contract has no language in
911/// it: a front end in any language generates these and reads a finding
912/// natively (#127).
913///
914/// Generated once from the desktop's Rust, then maintained here. Field numbers are
915/// the contract; add, never renumber.
916/// A parameter and its value, in the order a run chose them. Ordered, so a
917/// repeated field rather than a map.
918#[derive(serde::Serialize, serde::Deserialize)]
919#[derive(Clone, PartialEq, ::prost::Message)]
920pub struct NamedNumber {
921    #[prost(string, tag = "1")]
922    pub name: ::prost::alloc::string::String,
923    #[prost(double, tag = "2")]
924    pub value: f64,
925}
926#[derive(serde::Serialize, serde::Deserialize)]
927#[derive(Clone, Copy, PartialEq, ::prost::Message)]
928pub struct OptionalDouble {
929    #[prost(double, optional, tag = "1")]
930    pub value: ::core::option::Option<f64>,
931}
932/// One instrument's out-of-sample outcome under the panel's configuration.
933#[derive(serde::Serialize, serde::Deserialize)]
934#[derive(Clone, PartialEq, ::prost::Message)]
935pub struct OutcomeView {
936    #[prost(string, tag = "1")]
937    pub instrument: ::prost::alloc::string::String,
938    #[prost(double, tag = "2")]
939    pub strategy_return: f64,
940    #[prost(double, tag = "3")]
941    pub benchmark_return: f64,
942    #[prost(double, tag = "4")]
943    pub excess_return: f64,
944    #[prost(double, tag = "5")]
945    pub max_drawdown: f64,
946    #[prost(uint32, tag = "6")]
947    pub trades: u32,
948}
949/// A panel study, flattened for display.
950#[derive(serde::Serialize, serde::Deserialize)]
951#[derive(Clone, PartialEq, ::prost::Message)]
952pub struct PanelView {
953    /// The stored finding this view came from, so the window can ask for
954    /// something to be done to the record it is showing — a report composed
955    /// from it, a file kept with it. Empty for a view that was never
956    /// recorded. `default` because every view built before this had none.
957    #[prost(string, tag = "1")]
958    pub id: ::prost::alloc::string::String,
959    #[prost(string, tag = "2")]
960    pub verdict: ::prost::alloc::string::String,
961    #[prost(string, repeated, tag = "3")]
962    pub reasons: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
963    #[prost(message, repeated, tag = "4")]
964    pub recommendations: ::prost::alloc::vec::Vec<RecommendationView>,
965    #[prost(uint32, tag = "5")]
966    pub instruments: u32,
967    #[prost(uint32, tag = "6")]
968    pub total_trades: u32,
969    #[prost(double, tag = "7")]
970    pub mean_excess_return: f64,
971    #[prost(uint32, tag = "8")]
972    pub beat_benchmark: u32,
973    #[prost(double, tag = "9")]
974    pub mean_max_drawdown: f64,
975    #[prost(message, optional, tag = "10")]
976    pub breadth: ::core::option::Option<BreadthView>,
977    #[prost(message, optional, tag = "11")]
978    pub book: ::core::option::Option<BookView>,
979    #[prost(double, tag = "12")]
980    pub worst_max_drawdown: f64,
981    #[prost(uint32, tag = "13")]
982    pub trials: u32,
983    #[prost(double, tag = "14")]
984    pub best_sharpe: f64,
985    #[prost(double, optional, tag = "15")]
986    pub expected_best_under_null: ::core::option::Option<f64>,
987    #[prost(bool, tag = "16")]
988    pub survived_deflation: bool,
989    #[prost(string, tag = "17")]
990    pub in_sample: ::prost::alloc::string::String,
991    #[prost(string, tag = "18")]
992    pub out_of_sample: ::prost::alloc::string::String,
993    #[prost(message, repeated, tag = "19")]
994    pub selected_params: ::prost::alloc::vec::Vec<NamedNumber>,
995    #[prost(message, repeated, tag = "20")]
996    pub per_instrument: ::prost::alloc::vec::Vec<OutcomeView>,
997    #[prost(string, repeated, tag = "21")]
998    pub failures: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
999    #[prost(string, tag = "22")]
1000    pub dataset_version: ::prost::alloc::string::String,
1001    #[prost(string, tag = "23")]
1002    pub strategy_name: ::prost::alloc::string::String,
1003    #[prost(double, tag = "24")]
1004    pub starting_cash: f64,
1005    #[prost(double, tag = "25")]
1006    pub commission_bps: f64,
1007    #[prost(double, tag = "26")]
1008    pub slippage_bps: f64,
1009    #[prost(string, tag = "27")]
1010    pub engine: ::prost::alloc::string::String,
1011    /// The universe this panel ran over (#227); empty for the whole library.
1012    #[prost(string, tag = "28")]
1013    pub universe: ::prost::alloc::string::String,
1014    /// What to keep in mind about it: the reason it was chosen, its size as a
1015    /// search, that membership is today's, members left out for want of data.
1016    #[prost(string, repeated, tag = "29")]
1017    pub notes: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
1018}
1019/// One thing to do about a finding, flattened for display.
1020#[derive(serde::Serialize, serde::Deserialize)]
1021#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
1022pub struct RecommendationView {
1023    #[prost(string, tag = "1")]
1024    pub severity: ::prost::alloc::string::String,
1025    #[prost(string, tag = "2")]
1026    pub finding: ::prost::alloc::string::String,
1027    #[prost(string, tag = "3")]
1028    pub action: ::prost::alloc::string::String,
1029    #[prost(string, tag = "4")]
1030    pub evidence: ::prost::alloc::string::String,
1031}
1032/// A stored finding, reopened.
1033#[derive(serde::Serialize, serde::Deserialize)]
1034#[derive(Clone, PartialEq, ::prost::Message)]
1035pub struct RecordView {
1036    #[prost(oneof = "record_view::Of", tags = "1, 2, 3, 4")]
1037    pub of: ::core::option::Option<record_view::Of>,
1038}
1039/// Nested message and enum types in `RecordView`.
1040pub mod record_view {
1041    #[derive(serde::Serialize, serde::Deserialize)]
1042    #[derive(Clone, PartialEq, ::prost::Oneof)]
1043    pub enum Of {
1044        #[prost(message, tag = "1")]
1045        Study(super::StudyView),
1046        #[prost(message, tag = "2")]
1047        Panel(super::PanelView),
1048        #[prost(message, tag = "3")]
1049        Walkforward(super::WalkForwardView),
1050        #[prost(message, tag = "4")]
1051        Reported(super::ReportedView),
1052    }
1053}
1054/// What came back from re-running a stored finding.
1055///
1056/// A finding whose numbers cannot be regenerated is not evidence, so this is
1057/// the check the whole evidence store rests on.
1058#[derive(serde::Serialize, serde::Deserialize)]
1059#[derive(Clone, PartialEq, ::prost::Message)]
1060pub struct ReplayView {
1061    /// `reproduced`, `data-changed`, `engine-changed`, `diverged`,
1062    /// `not-replayable` or `failed`. A slug so the window can style it
1063    /// without parsing prose.
1064    #[prost(string, tag = "1")]
1065    pub outcome: ::prost::alloc::string::String,
1066    /// Whether the finding still stands. Only a clean reproduction counts:
1067    /// changed data or a changed engine leaves the claim untested, which is
1068    /// not the same as confirmed.
1069    #[prost(bool, tag = "2")]
1070    pub holds: bool,
1071    #[prost(string, tag = "3")]
1072    pub detail: ::prost::alloc::string::String,
1073    #[prost(message, optional, tag = "4")]
1074    pub divergence: ::core::option::Option<DivergenceView>,
1075}
1076/// A finding an engine Arvo did not run computed, judged by Arvo
1077/// (ADR-0026). Shown as what it is: the numbers and the verdict, and where
1078/// the data lives.
1079#[derive(serde::Serialize, serde::Deserialize)]
1080#[derive(Clone, PartialEq, ::prost::Message)]
1081pub struct ReportedView {
1082    #[prost(string, tag = "1")]
1083    pub id: ::prost::alloc::string::String,
1084    #[prost(string, tag = "2")]
1085    pub hypothesis: ::prost::alloc::string::String,
1086    #[prost(string, tag = "3")]
1087    pub claim: ::prost::alloc::string::String,
1088    #[prost(string, tag = "4")]
1089    pub instrument: ::prost::alloc::string::String,
1090    #[prost(string, tag = "5")]
1091    pub engine: ::prost::alloc::string::String,
1092    #[prost(string, tag = "6")]
1093    pub from: ::prost::alloc::string::String,
1094    #[prost(string, tag = "7")]
1095    pub to: ::prost::alloc::string::String,
1096    #[prost(string, tag = "8")]
1097    pub interval: ::prost::alloc::string::String,
1098    /// `id@version`: the engine's own dataset, not in Arvo's library.
1099    #[prost(string, tag = "9")]
1100    pub dataset: ::prost::alloc::string::String,
1101    #[prost(string, tag = "10")]
1102    pub strategy: ::prost::alloc::string::String,
1103    #[prost(string, tag = "11")]
1104    pub verdict: ::prost::alloc::string::String,
1105    #[prost(string, repeated, tag = "12")]
1106    pub reasons: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
1107    #[prost(uint32, tag = "13")]
1108    pub trades: u32,
1109    #[prost(uint32, optional, tag = "14")]
1110    pub trials: ::core::option::Option<u32>,
1111    /// Absent when there was no benchmark to judge against.
1112    #[prost(double, optional, tag = "15")]
1113    pub total_return: ::core::option::Option<f64>,
1114    #[prost(double, optional, tag = "16")]
1115    pub excess_return: ::core::option::Option<f64>,
1116    #[prost(double, optional, tag = "17")]
1117    pub sharpe: ::core::option::Option<f64>,
1118    #[prost(double, optional, tag = "18")]
1119    pub max_drawdown: ::core::option::Option<f64>,
1120    #[prost(string, tag = "19")]
1121    pub recorded_at: ::prost::alloc::string::String,
1122    #[prost(string, tag = "20")]
1123    pub author: ::prost::alloc::string::String,
1124    /// The files kept with it: a report, a figure, the trades (#157).
1125    #[prost(message, repeated, tag = "21")]
1126    pub attachments: ::prost::alloc::vec::Vec<Attachment>,
1127}
1128/// How much one parameter moved across the folds.
1129#[derive(serde::Serialize, serde::Deserialize)]
1130#[derive(Clone, PartialEq, ::prost::Message)]
1131pub struct StabilityView {
1132    #[prost(string, tag = "1")]
1133    pub axis: ::prost::alloc::string::String,
1134    #[prost(uint32, tag = "2")]
1135    pub distinct: u32,
1136    #[prost(double, tag = "3")]
1137    pub modal: f64,
1138    #[prost(double, tag = "4")]
1139    pub modal_share: f64,
1140}
1141/// The full result of a study, flattened for display.
1142#[derive(serde::Serialize, serde::Deserialize)]
1143#[derive(Clone, PartialEq, ::prost::Message)]
1144pub struct StudyView {
1145    /// The stored finding this view came from, so the window can ask for
1146    /// something to be done to the record it is showing — a report composed
1147    /// from it, a file kept with it. Empty for a view that was never
1148    /// recorded. `default` because every view built before this had none.
1149    #[prost(string, tag = "1")]
1150    pub id: ::prost::alloc::string::String,
1151    #[prost(string, tag = "2")]
1152    pub instrument: ::prost::alloc::string::String,
1153    /// Every instrument the run held, head first.
1154    ///
1155    /// One entry is an ordinary study. More than one is a book: the same rule
1156    /// on several instruments settling against a single account, where a
1157    /// position one member takes is capital another cannot have.
1158    ///
1159    /// `default` because this is a persisted format — a finding recorded
1160    /// before a run could hold more than one loads as what it was.
1161    #[prost(string, repeated, tag = "3")]
1162    pub instruments: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
1163    /// What each member of a book contributed. Empty for a single study.
1164    #[prost(message, repeated, tag = "4")]
1165    pub members: ::prost::alloc::vec::Vec<MemberView>,
1166    #[prost(string, tag = "5")]
1167    pub verdict: ::prost::alloc::string::String,
1168    #[prost(string, repeated, tag = "6")]
1169    pub reasons: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
1170    #[prost(uint32, tag = "7")]
1171    pub trials: u32,
1172    /// Configurations tried elsewhere before a shared experiment arrived,
1173    /// counted into the bar. `default`: a view built before sharing had none.
1174    #[prost(uint32, tag = "8")]
1175    pub prior_trials: u32,
1176    #[prost(double, tag = "9")]
1177    pub best_sharpe: f64,
1178    #[prost(double, optional, tag = "10")]
1179    pub expected_best_under_null: ::core::option::Option<f64>,
1180    #[prost(bool, tag = "11")]
1181    pub survived_deflation: bool,
1182    /// Every configuration the search tried, not only the one it picked.
1183    #[prost(message, optional, tag = "12")]
1184    pub surface: ::core::option::Option<SurfaceView>,
1185    #[prost(string, tag = "13")]
1186    pub in_sample: ::prost::alloc::string::String,
1187    #[prost(string, tag = "14")]
1188    pub out_of_sample: ::prost::alloc::string::String,
1189    #[prost(message, repeated, tag = "15")]
1190    pub selected_params: ::prost::alloc::vec::Vec<NamedNumber>,
1191    #[prost(message, optional, tag = "16")]
1192    pub strategy: ::core::option::Option<MetricsView>,
1193    #[prost(message, optional, tag = "17")]
1194    pub benchmark: ::core::option::Option<MetricsView>,
1195    /// Uncorrected. \[`Self::dividend_gap`\] sits beside it rather than being
1196    /// folded in: a number silently adjusted by something the reader cannot see
1197    /// is what this platform exists to stop.
1198    #[prost(double, tag = "18")]
1199    pub excess_return: f64,
1200    /// How much of that margin is the dividend bias rather than skill.
1201    ///
1202    /// `None` means it was never measured — no distribution series is held for
1203    /// these instruments. The bias is real either way; only its size is
1204    /// unknown, and `recommendations` says so.
1205    #[prost(message, optional, tag = "19")]
1206    pub dividend_gap: ::core::option::Option<DividendGapView>,
1207    /// Both sides after capital-gains tax at stated rates, sold at the end
1208    /// (#26). Beside the excess return for the same reason as the dividend
1209    /// gap. `None` when the curves are too short to tax.
1210    #[prost(message, optional, tag = "20")]
1211    pub after_tax: ::core::option::Option<AfterTaxView>,
1212    /// The two curves behind the numbers. A table says a strategy returned
1213    /// less than the market; a chart says whether it did so steadily or lost
1214    /// it all in one month, and those are different findings.
1215    #[prost(message, repeated, tag = "21")]
1216    pub strategy_curve: ::prost::alloc::vec::Vec<CurvePoint>,
1217    #[prost(message, repeated, tag = "22")]
1218    pub benchmark_curve: ::prost::alloc::vec::Vec<CurvePoint>,
1219    /// The instrument's own bars over the judged period, with every entry and
1220    /// exit marked on them.
1221    ///
1222    /// The view that shows what the rule *did* rather than what it added up
1223    /// to. A summary cannot say the entries all landed on three days, or that
1224    /// every winner came out of one gap; this says it at a glance.
1225    #[prost(message, repeated, tag = "23")]
1226    pub price: ::prost::alloc::vec::Vec<CandlePoint>,
1227    #[prost(message, repeated, tag = "24")]
1228    pub markers: ::prost::alloc::vec::Vec<TradeMarkerView>,
1229    /// The rest of a book's instruments, each with its own bars and its own
1230    /// trades. Empty for a single study.
1231    ///
1232    /// The head instrument is deliberately *not* repeated here — it is already
1233    /// \[`Self::price`\] and \[`Self::markers`\] above. The shape mirrors
1234    /// `Experiment.alongside`, which is head plus the rest for the same reason:
1235    /// carrying the head twice would put a third of a three-member book's
1236    /// candles on the wire for nothing.
1237    #[prost(message, repeated, tag = "25")]
1238    pub alongside_charts: ::prost::alloc::vec::Vec<InstrumentChartView>,
1239    /// Depth below the running peak, as a percentage.
1240    #[prost(message, repeated, tag = "26")]
1241    pub underwater: ::prost::alloc::vec::Vec<CurvePoint>,
1242    /// What is wrong with the bars this was produced from.
1243    #[prost(message, repeated, tag = "27")]
1244    pub data_findings: ::prost::alloc::vec::Vec<
1245        super::super::market::v1::DataFindingView,
1246    >,
1247    /// Every round trip, so nobody has to take the summary on trust.
1248    #[prost(message, repeated, tag = "28")]
1249    pub trades: ::prost::alloc::vec::Vec<TradeRowView>,
1250    /// Month-by-month, so a total return can be read as steady or as one
1251    /// lucky quarter. Derived from the same curve, not a second measurement.
1252    #[prost(message, repeated, tag = "29")]
1253    pub monthly: ::prost::alloc::vec::Vec<MonthlyReturnView>,
1254    /// The round trips behind the return, and what they cost.
1255    #[prost(message, optional, tag = "30")]
1256    pub trades_detail: ::core::option::Option<TradesView>,
1257    /// What to do about this finding, most stopping first.
1258    ///
1259    /// Derived on read rather than stored, so a finding pulled out of memory
1260    /// is read against today's rules rather than the ones in force when it
1261    /// was recorded.
1262    #[prost(message, repeated, tag = "31")]
1263    pub recommendations: ::prost::alloc::vec::Vec<RecommendationView>,
1264    /// The dataset this result was produced from, as a content hash. Compared
1265    /// against the live one to decide whether the result is still current.
1266    #[prost(string, tag = "32")]
1267    pub dataset_version: ::prost::alloc::string::String,
1268    #[prost(string, tag = "33")]
1269    pub strategy_name: ::prost::alloc::string::String,
1270    #[prost(double, tag = "34")]
1271    pub starting_cash: f64,
1272    #[prost(double, tag = "35")]
1273    pub commission_bps: f64,
1274    #[prost(double, tag = "36")]
1275    pub slippage_bps: f64,
1276    #[prost(string, tag = "37")]
1277    pub engine: ::prost::alloc::string::String,
1278}
1279/// One configuration's cell on the search surface.
1280#[derive(serde::Serialize, serde::Deserialize)]
1281#[derive(Clone, Copy, PartialEq, ::prost::Message)]
1282pub struct SurfaceCell {
1283    #[prost(double, tag = "1")]
1284    pub x: f64,
1285    #[prost(double, tag = "2")]
1286    pub y: f64,
1287    #[prost(double, tag = "3")]
1288    pub sharpe: f64,
1289    /// Whether this is the configuration that was chosen.
1290    #[prost(bool, tag = "4")]
1291    pub selected: bool,
1292    /// Whether it beat what a no-skill search of this size would produce.
1293    ///
1294    /// The distinction the whole chart is drawn around. A cell below the bar
1295    /// is not a weak result, it is *not a result* — a score a coin-flipping
1296    /// search of the same size would have been expected to reach anyway.
1297    #[prost(bool, tag = "5")]
1298    pub above_null: bool,
1299}
1300/// The in-sample score of every configuration the search tried.
1301///
1302/// # Why this exists at all
1303///
1304/// Reporting only the winner shows two completely different situations
1305/// identically: a broad region of configurations that all scored well, which
1306/// suggests something real and robust to the exact parameters; and one bright
1307/// cell surrounded by nothing, which is what fitting noise looks like from
1308/// above. The verdict machinery already deflates for the *size* of the search;
1309/// this is the part a person has to look at.
1310#[derive(serde::Serialize, serde::Deserialize)]
1311#[derive(Clone, PartialEq, ::prost::Message)]
1312pub struct SurfaceView {
1313    /// The two axes drawn, by name.
1314    #[prost(string, tag = "1")]
1315    pub x_axis: ::prost::alloc::string::String,
1316    #[prost(string, tag = "2")]
1317    pub y_axis: ::prost::alloc::string::String,
1318    #[prost(double, repeated, tag = "3")]
1319    pub x_values: ::prost::alloc::vec::Vec<f64>,
1320    #[prost(double, repeated, tag = "4")]
1321    pub y_values: ::prost::alloc::vec::Vec<f64>,
1322    #[prost(message, repeated, tag = "5")]
1323    pub cells: ::prost::alloc::vec::Vec<SurfaceCell>,
1324    /// The bar a cell has to clear to be worth anything. `None` when there
1325    /// were too few trials to say.
1326    #[prost(double, optional, tag = "6")]
1327    pub null_bar: ::core::option::Option<f64>,
1328    #[prost(double, tag = "7")]
1329    pub best: f64,
1330    /// Axes not drawn, because a surface has two dimensions and a grid may
1331    /// have more. Named so nobody reads the chart as the whole search.
1332    #[prost(string, repeated, tag = "8")]
1333    pub collapsed: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
1334}
1335/// Where a trade happened, to be drawn on the price.
1336#[derive(serde::Serialize, serde::Deserialize)]
1337#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
1338pub struct TradeMarkerView {
1339    #[prost(int64, tag = "1")]
1340    pub time: i64,
1341    /// `entry` or `exit`; the chart decides shape and side from it.
1342    #[prost(string, tag = "2")]
1343    pub kind: ::prost::alloc::string::String,
1344    /// `stop` or `signal` for an exit, so a stop-out is visually distinct
1345    /// from a rule that chose to leave. Empty for an entry.
1346    #[prost(string, tag = "3")]
1347    pub reason: ::prost::alloc::string::String,
1348    #[prost(string, tag = "4")]
1349    pub label: ::prost::alloc::string::String,
1350}
1351/// One round trip, as a table row.
1352///
1353/// Every field the ledger holds, because the point of a table is that nobody
1354/// has to decide in advance which column someone will want to sort by. The
1355/// aggregate statistics above it answer "how did it do"; this answers "what
1356/// did it actually do", and those are different questions with different
1357/// failure modes — an expectancy of +£300 built from one +£9,000 trade and
1358/// nineteen losses is a fact only the rows show.
1359#[derive(serde::Serialize, serde::Deserialize)]
1360#[derive(Clone, PartialEq, ::prost::Message)]
1361pub struct TradeRowView {
1362    /// Which instrument this round trip was in.
1363    ///
1364    /// Empty for a run recorded before the ledger named instruments, and for
1365    /// an ordinary single-instrument study where the subject line already
1366    /// says it. Only a book shows the column.
1367    #[prost(string, tag = "1")]
1368    pub instrument: ::prost::alloc::string::String,
1369    #[prost(string, tag = "2")]
1370    pub opened: ::prost::alloc::string::String,
1371    /// Empty while the position is still open at the end of the run.
1372    #[prost(string, tag = "3")]
1373    pub closed: ::prost::alloc::string::String,
1374    #[prost(string, tag = "4")]
1375    pub direction: ::prost::alloc::string::String,
1376    #[prost(double, tag = "5")]
1377    pub quantity: f64,
1378    #[prost(double, tag = "6")]
1379    pub entry: f64,
1380    /// `None` while still open, so the table shows a gap rather than a price
1381    /// nobody traded at.
1382    #[prost(double, optional, tag = "7")]
1383    pub exit: ::core::option::Option<f64>,
1384    #[prost(double, tag = "8")]
1385    pub pnl: f64,
1386    #[prost(double, tag = "9")]
1387    pub commission: f64,
1388    /// Days held. Fractional, because an intraday trade held forty minutes is
1389    /// not "0 days" — it is 0.03, and rounding it away would make every
1390    /// intraday ledger look like a column of zeroes.
1391    #[prost(double, optional, tag = "10")]
1392    pub held_days: ::core::option::Option<f64>,
1393    /// `signal`, `stop`, or `open`.
1394    #[prost(string, tag = "11")]
1395    pub exit_reason: ::prost::alloc::string::String,
1396    /// What the rule saw at entry (#190): the condition that fired, the value it
1397    /// was judged on, the regime it saw, and the quantity it asked the gate for.
1398    /// Absent on a trade recorded before the journal or opened by no rule.
1399    #[prost(string, optional, tag = "12")]
1400    pub rule: ::core::option::Option<::prost::alloc::string::String>,
1401    #[prost(double, optional, tag = "13")]
1402    pub signal: ::core::option::Option<f64>,
1403    #[prost(string, optional, tag = "14")]
1404    pub regime: ::core::option::Option<::prost::alloc::string::String>,
1405    #[prost(double, optional, tag = "15")]
1406    pub asked: ::core::option::Option<f64>,
1407}
1408/// What the round trips looked like, flattened for display.
1409///
1410/// Beside the metrics rather than inside them: metrics come from the equity
1411/// curve and these come from the ledger, and keeping the two apart is what
1412/// makes it obvious which is which when they say different things.
1413#[derive(serde::Serialize, serde::Deserialize)]
1414#[derive(Clone, Copy, PartialEq, ::prost::Message)]
1415pub struct TradesView {
1416    #[prost(uint32, tag = "1")]
1417    pub closed: u32,
1418    #[prost(uint32, tag = "2")]
1419    pub still_open: u32,
1420    #[prost(double, optional, tag = "3")]
1421    pub win_rate: ::core::option::Option<f64>,
1422    #[prost(double, optional, tag = "4")]
1423    pub profit_factor: ::core::option::Option<f64>,
1424    #[prost(double, optional, tag = "5")]
1425    pub expectancy: ::core::option::Option<f64>,
1426    #[prost(double, optional, tag = "6")]
1427    pub average_win: ::core::option::Option<f64>,
1428    #[prost(double, optional, tag = "7")]
1429    pub average_loss: ::core::option::Option<f64>,
1430    /// Mean holding period in days, which is the unit a reader thinks in and
1431    /// the one that decides short- versus long-term tax treatment.
1432    #[prost(double, optional, tag = "8")]
1433    pub average_holding_days: ::core::option::Option<f64>,
1434    /// Every fee and commission the venue charged, in account currency.
1435    ///
1436    /// Fees only — slippage is charged inside the fill prices and is already
1437    /// reflected in the return, not here. Naming this `fees` rather than
1438    /// `cost` is the whole point: a reader who took it for the total cost of
1439    /// trading would be short by the spread assumption on every round trip.
1440    #[prost(double, tag = "9")]
1441    pub fees_paid: f64,
1442    /// Fees as a fraction of starting capital, so they can be read against
1443    /// the return directly.
1444    #[prost(double, tag = "10")]
1445    pub fees_fraction: f64,
1446    #[prost(uint32, tag = "11")]
1447    pub signal_exits: u32,
1448    #[prost(uint32, tag = "12")]
1449    pub stop_exits: u32,
1450}
1451/// A finding that could not be read, and why.
1452///
1453/// Shown rather than logged. Four findings were once lost to a field rename
1454/// and the only trace was a warning nobody had reason to look at — a research
1455/// store that quietly forgets things is worse than one that says it has.
1456#[derive(serde::Serialize, serde::Deserialize)]
1457#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
1458pub struct UnreadableView {
1459    #[prost(string, tag = "1")]
1460    pub id: ::prost::alloc::string::String,
1461    #[prost(string, tag = "2")]
1462    pub reason: ::prost::alloc::string::String,
1463}
1464/// A walk-forward run, flattened for display.
1465#[derive(serde::Serialize, serde::Deserialize)]
1466#[derive(Clone, PartialEq, ::prost::Message)]
1467pub struct WalkForwardView {
1468    /// The stored finding this view came from, so the window can ask for
1469    /// something to be done to the record it is showing — a report composed
1470    /// from it, a file kept with it. Empty for a view that was never
1471    /// recorded. `default` because every view built before this had none.
1472    #[prost(string, tag = "1")]
1473    pub id: ::prost::alloc::string::String,
1474    #[prost(string, tag = "2")]
1475    pub instrument: ::prost::alloc::string::String,
1476    #[prost(string, tag = "3")]
1477    pub verdict: ::prost::alloc::string::String,
1478    #[prost(string, repeated, tag = "4")]
1479    pub reasons: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
1480    #[prost(message, repeated, tag = "5")]
1481    pub recommendations: ::prost::alloc::vec::Vec<RecommendationView>,
1482    #[prost(message, repeated, tag = "6")]
1483    pub folds: ::prost::alloc::vec::Vec<FoldView>,
1484    #[prost(uint32, tag = "7")]
1485    pub folds_surviving_deflation: u32,
1486    /// Folds in which the selected configuration never opened a position — the
1487    /// symptom of a step too short for the rule's warm-up.
1488    #[prost(uint32, tag = "8")]
1489    pub folds_without_trades: u32,
1490    /// How the selection moved. The thing only a rolling procedure can show.
1491    #[prost(message, repeated, tag = "9")]
1492    pub stability: ::prost::alloc::vec::Vec<StabilityView>,
1493    #[prost(message, optional, tag = "10")]
1494    pub strategy: ::core::option::Option<MetricsView>,
1495    #[prost(message, optional, tag = "11")]
1496    pub benchmark: ::core::option::Option<MetricsView>,
1497    #[prost(double, tag = "12")]
1498    pub excess_return: f64,
1499    #[prost(message, repeated, tag = "13")]
1500    pub strategy_curve: ::prost::alloc::vec::Vec<CurvePoint>,
1501    #[prost(message, repeated, tag = "14")]
1502    pub benchmark_curve: ::prost::alloc::vec::Vec<CurvePoint>,
1503    #[prost(message, repeated, tag = "15")]
1504    pub price: ::prost::alloc::vec::Vec<CandlePoint>,
1505    #[prost(message, repeated, tag = "16")]
1506    pub markers: ::prost::alloc::vec::Vec<TradeMarkerView>,
1507    #[prost(message, repeated, tag = "17")]
1508    pub underwater: ::prost::alloc::vec::Vec<CurvePoint>,
1509    /// What is wrong with the bars this was produced from.
1510    #[prost(message, repeated, tag = "18")]
1511    pub data_findings: ::prost::alloc::vec::Vec<
1512        super::super::market::v1::DataFindingView,
1513    >,
1514    #[prost(message, repeated, tag = "19")]
1515    pub trades: ::prost::alloc::vec::Vec<TradeRowView>,
1516    #[prost(message, optional, tag = "20")]
1517    pub trades_detail: ::core::option::Option<TradesView>,
1518    #[prost(int64, tag = "21")]
1519    pub in_sample_days: i64,
1520    #[prost(int64, tag = "22")]
1521    pub step_days: i64,
1522    #[prost(bool, tag = "23")]
1523    pub anchored: bool,
1524    #[prost(string, tag = "24")]
1525    pub dataset_version: ::prost::alloc::string::String,
1526    #[prost(string, tag = "25")]
1527    pub strategy_name: ::prost::alloc::string::String,
1528    #[prost(double, tag = "26")]
1529    pub starting_cash: f64,
1530    #[prost(double, tag = "27")]
1531    pub commission_bps: f64,
1532    #[prost(double, tag = "28")]
1533    pub slippage_bps: f64,
1534    #[prost(string, tag = "29")]
1535    pub engine: ::prost::alloc::string::String,
1536}
1537/// One bar, as the chart wants it.
1538/// The review after the close (#217): what the day's sessions did, read back
1539/// off their records, as a person reads it.
1540#[derive(serde::Serialize, serde::Deserialize)]
1541#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
1542pub struct ReviewView {
1543    #[prost(string, tag = "1")]
1544    pub day: ::prost::alloc::string::String,
1545    #[prost(string, tag = "2")]
1546    pub markdown: ::prost::alloc::string::String,
1547    /// Where it is written under the project, so it can be opened.
1548    #[prost(string, tag = "3")]
1549    pub path: ::prost::alloc::string::String,
1550    /// Whether this call wrote it, or read one written before.
1551    #[prost(bool, tag = "4")]
1552    pub written_now: bool,
1553}
1554/// One bar of the library, as the research tier reads it (#195).
1555#[derive(serde::Serialize, serde::Deserialize)]
1556#[derive(Clone, PartialEq, ::prost::Message)]
1557pub struct BarView {
1558    /// When the bar opened.
1559    #[prost(string, tag = "1")]
1560    pub at: ::prost::alloc::string::String,
1561    #[prost(double, tag = "2")]
1562    pub open: f64,
1563    #[prost(double, tag = "3")]
1564    pub high: f64,
1565    #[prost(double, tag = "4")]
1566    pub low: f64,
1567    #[prost(double, tag = "5")]
1568    pub close: f64,
1569    #[prost(double, tag = "6")]
1570    pub volume: f64,
1571}
1572#[derive(serde::Serialize, serde::Deserialize)]
1573#[derive(Clone, PartialEq, ::prost::Message)]
1574pub struct BarsView {
1575    #[prost(string, tag = "1")]
1576    pub instrument: ::prost::alloc::string::String,
1577    #[prost(string, tag = "2")]
1578    pub interval: ::prost::alloc::string::String,
1579    #[prost(message, repeated, tag = "3")]
1580    pub bars: ::prost::alloc::vec::Vec<BarView>,
1581}
1582/// The regime each bar closed in: "trending up", "trending down" or
1583/// "ranging", absent until the lookback has filled. Labelled after the fact
1584/// over the closes, so it says what the market was doing and not what a rule
1585/// could have known at the time.
1586#[derive(serde::Serialize, serde::Deserialize)]
1587#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
1588pub struct RegimePointView {
1589    #[prost(string, tag = "1")]
1590    pub at: ::prost::alloc::string::String,
1591    #[prost(string, optional, tag = "2")]
1592    pub regime: ::core::option::Option<::prost::alloc::string::String>,
1593}
1594#[derive(serde::Serialize, serde::Deserialize)]
1595#[derive(Clone, PartialEq, ::prost::Message)]
1596pub struct RegimeView {
1597    #[prost(string, tag = "1")]
1598    pub instrument: ::prost::alloc::string::String,
1599    #[prost(string, tag = "2")]
1600    pub interval: ::prost::alloc::string::String,
1601    /// Bars each label looks back over.
1602    #[prost(uint32, tag = "3")]
1603    pub lookback: u32,
1604    /// The last bar's regime, when it has one.
1605    #[prost(string, optional, tag = "4")]
1606    pub current: ::core::option::Option<::prost::alloc::string::String>,
1607    #[prost(message, repeated, tag = "5")]
1608    pub points: ::prost::alloc::vec::Vec<RegimePointView>,
1609    /// How many of the returned bars were in each regime.
1610    #[prost(map = "string, uint32", tag = "6")]
1611    pub shares: ::std::collections::HashMap<::prost::alloc::string::String, u32>,
1612}
1613#[derive(serde::Serialize, serde::Deserialize)]
1614#[derive(Clone, Copy, PartialEq, ::prost::Message)]
1615pub struct CandlePoint {
1616    #[prost(int64, tag = "1")]
1617    pub time: i64,
1618    #[prost(double, tag = "2")]
1619    pub open: f64,
1620    #[prost(double, tag = "3")]
1621    pub high: f64,
1622    #[prost(double, tag = "4")]
1623    pub low: f64,
1624    #[prost(double, tag = "5")]
1625    pub close: f64,
1626}
1627/// One instrument's bars with its own trades marked on them.
1628///
1629/// A book holds several, and each chart must show only the round trips that
1630/// happened in *its* instrument. Plotting the whole ledger on one member's
1631/// prices puts entries on days that instrument never traded.
1632#[derive(serde::Serialize, serde::Deserialize)]
1633#[derive(Clone, PartialEq, ::prost::Message)]
1634pub struct InstrumentChartView {
1635    #[prost(string, tag = "1")]
1636    pub instrument: ::prost::alloc::string::String,
1637    #[prost(message, repeated, tag = "2")]
1638    pub price: ::prost::alloc::vec::Vec<CandlePoint>,
1639    #[prost(message, repeated, tag = "3")]
1640    pub markers: ::prost::alloc::vec::Vec<TradeMarkerView>,
1641}
1642/// A finding's caveat, against the script line that produced the finding:
1643/// what the Problems panel lists with source "Arvo" (ADR-0021).
1644#[derive(serde::Serialize, serde::Deserialize)]
1645#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
1646pub struct ResearchProblemView {
1647    /// Relative to the project folder.
1648    #[prost(string, tag = "1")]
1649    pub path: ::prost::alloc::string::String,
1650    #[prost(uint32, tag = "2")]
1651    pub line: u32,
1652    /// `error`, `warning` or `info`.
1653    #[prost(string, tag = "3")]
1654    pub severity: ::prost::alloc::string::String,
1655    #[prost(string, tag = "4")]
1656    pub message: ::prost::alloc::string::String,
1657    /// The finding it comes from, to open.
1658    #[prost(string, tag = "5")]
1659    pub finding: ::prost::alloc::string::String,
1660}
1661/// The Problems panel's rows.
1662///
1663/// A list, as a message: an RPC returns one message, never a bare repeated
1664/// field, so a call that answers with a list needs a name for the list.
1665#[derive(serde::Serialize, serde::Deserialize)]
1666#[derive(Clone, PartialEq, ::prost::Message)]
1667pub struct ProblemsView {
1668    #[prost(message, repeated, tag = "1")]
1669    pub problems: ::prost::alloc::vec::Vec<ResearchProblemView>,
1670}
1671/// One finding on the leaderboard (#226).
1672#[derive(serde::Serialize, serde::Deserialize)]
1673#[derive(Clone, PartialEq, ::prost::Message)]
1674pub struct RankingRowView {
1675    #[prost(string, tag = "1")]
1676    pub id: ::prost::alloc::string::String,
1677    #[prost(string, tag = "2")]
1678    pub subject: ::prost::alloc::string::String,
1679    #[prost(string, tag = "3")]
1680    pub kind: ::prost::alloc::string::String,
1681    #[prost(string, tag = "4")]
1682    pub rule: ::prost::alloc::string::String,
1683    #[prost(string, tag = "5")]
1684    pub interval: ::prost::alloc::string::String,
1685    #[prost(string, tag = "6")]
1686    pub verdict: ::prost::alloc::string::String,
1687    /// The verdict under the conservative cost tier, or empty when the run
1688    /// was not asked: a result refused on its own terms is not.
1689    #[prost(string, tag = "7")]
1690    pub conservative_verdict: ::prost::alloc::string::String,
1691    /// Mean profit per closed out-of-sample trade: under the conservative tier
1692    /// when it was measured, otherwise under the stated costs.
1693    #[prost(double, tag = "8")]
1694    pub expectancy: f64,
1695    /// "conservative" or "stated": which costs the expectancy is under.
1696    #[prost(string, tag = "9")]
1697    pub expectancy_costs: ::prost::alloc::string::String,
1698    /// Read it; it is never the order.
1699    #[prost(double, tag = "10")]
1700    pub total_return: f64,
1701    #[prost(double, tag = "11")]
1702    pub max_drawdown: f64,
1703    #[prost(uint32, tag = "12")]
1704    pub trades: u32,
1705    /// The regimes the out-of-sample trades opened in, distinct.
1706    #[prost(string, repeated, tag = "13")]
1707    pub regimes: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
1708    /// How many configurations the search tried, when the finding says.
1709    #[prost(uint32, optional, tag = "14")]
1710    pub search: ::core::option::Option<u32>,
1711    #[prost(string, tag = "15")]
1712    pub recorded_at: ::prost::alloc::string::String,
1713    #[prost(bool, optional, tag = "16")]
1714    pub stale: ::core::option::Option<bool>,
1715}
1716/// The leaderboard (#226): findings in one order and only one. Supported
1717/// under the conservative tier first, by that expectancy; then Supported
1718/// under the stated costs where the conservative tier was never measured;
1719/// then everything else, whatever its number. Ties by drawdown, then trades.
1720#[derive(serde::Serialize, serde::Deserialize)]
1721#[derive(Clone, PartialEq, ::prost::Message)]
1722pub struct Ranking {
1723    #[prost(message, repeated, tag = "1")]
1724    pub rows: ::prost::alloc::vec::Vec<RankingRowView>,
1725    /// Findings that could not be read, and why.
1726    #[prost(string, repeated, tag = "2")]
1727    pub notes: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
1728}