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