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