arvo-client 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. arvo/__init__.py +468 -0
  2. arvo/common/__init__.py +0 -0
  3. arvo/common/v1/__init__.py +0 -0
  4. arvo/common/v1/models_pb2.py +38 -0
  5. arvo/common/v1/models_pb2_grpc.py +24 -0
  6. arvo/market/__init__.py +0 -0
  7. arvo/market/v1/__init__.py +0 -0
  8. arvo/market/v1/models_pb2.py +46 -0
  9. arvo/market/v1/models_pb2_grpc.py +24 -0
  10. arvo/market/v1/views_pb2.py +56 -0
  11. arvo/market/v1/views_pb2_grpc.py +24 -0
  12. arvo/platform/__init__.py +0 -0
  13. arvo/platform/v1/__init__.py +0 -0
  14. arvo/platform/v1/models_pb2.py +50 -0
  15. arvo/platform/v1/models_pb2_grpc.py +24 -0
  16. arvo/platform/v1/views_pb2.py +90 -0
  17. arvo/platform/v1/views_pb2_grpc.py +24 -0
  18. arvo/portfolio/__init__.py +0 -0
  19. arvo/portfolio/v1/__init__.py +0 -0
  20. arvo/portfolio/v1/models_pb2.py +36 -0
  21. arvo/portfolio/v1/models_pb2_grpc.py +24 -0
  22. arvo/portfolio/v1/views_pb2.py +48 -0
  23. arvo/portfolio/v1/views_pb2_grpc.py +24 -0
  24. arvo/py.typed +0 -0
  25. arvo/research/__init__.py +0 -0
  26. arvo/research/v1/__init__.py +0 -0
  27. arvo/research/v1/models_pb2.py +100 -0
  28. arvo/research/v1/models_pb2_grpc.py +24 -0
  29. arvo/research/v1/views_pb2.py +110 -0
  30. arvo/research/v1/views_pb2_grpc.py +24 -0
  31. arvo/services/__init__.py +0 -0
  32. arvo/services/v1/__init__.py +0 -0
  33. arvo/services/v1/accounts_pb2.py +39 -0
  34. arvo/services/v1/accounts_pb2_grpc.py +246 -0
  35. arvo/services/v1/market_pb2.py +39 -0
  36. arvo/services/v1/market_pb2_grpc.py +375 -0
  37. arvo/services/v1/platform_pb2.py +39 -0
  38. arvo/services/v1/platform_pb2_grpc.py +377 -0
  39. arvo/services/v1/portfolio_pb2.py +39 -0
  40. arvo/services/v1/portfolio_pb2_grpc.py +202 -0
  41. arvo/services/v1/research_pb2.py +43 -0
  42. arvo/services/v1/research_pb2_grpc.py +1372 -0
  43. arvo/services/v1/scripts_pb2.py +39 -0
  44. arvo/services/v1/scripts_pb2_grpc.py +357 -0
  45. arvo/services/v1/sessions_pb2.py +38 -0
  46. arvo/services/v1/sessions_pb2_grpc.py +369 -0
  47. arvo/session/__init__.py +0 -0
  48. arvo/session/v1/__init__.py +0 -0
  49. arvo/session/v1/models_pb2.py +44 -0
  50. arvo/session/v1/models_pb2_grpc.py +24 -0
  51. arvo_client-0.1.0.dist-info/METADATA +115 -0
  52. arvo_client-0.1.0.dist-info/RECORD +53 -0
  53. arvo_client-0.1.0.dist-info/WHEEL +4 -0
arvo/__init__.py ADDED
@@ -0,0 +1,468 @@
1
+ """Arvo's research from Python.
2
+
3
+ import arvo
4
+
5
+ engine = arvo.connect()
6
+ for plan in engine.strategies():
7
+ print(plan.name, plan.premise)
8
+
9
+ found = engine.run_study("AAPL.RH", "sma_cross", author="script:first-look")
10
+ print(found.verdict, found.read_this_first)
11
+ for item in found.advice:
12
+ print(item.severity, item.finding)
13
+
14
+ prices = engine.bars("AAPL.RH")
15
+
16
+ This talks to a running ``arvo-engine`` through its research API (ADR-0018),
17
+ which reads research memory and runs research. Nothing here fetches data,
18
+ touches a credential or places an order, and nothing can: the API has no call
19
+ that would.
20
+
21
+ Every run needs an ``author``. It is saved as that author's finding and held to
22
+ the no-skill bar for **everything that author has run**, so a script that
23
+ tries configurations until one passes does not make it pass. Read ``verdict``,
24
+ ``read_this_first`` and ``advice`` before any number in ``detail``.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import json
30
+ import os
31
+ import re
32
+ from dataclasses import dataclass, field
33
+ from pathlib import Path
34
+ from typing import Any, Sequence
35
+
36
+ import grpc
37
+
38
+ # The contract is split by domain (arvo-engine-api): one package per subject,
39
+ # holding its models and its views, with the services apart from them. `pb`
40
+ # keeps its old name so the rest of this module reads the same.
41
+ from arvo.common.v1 import models_pb2 as common
42
+ from arvo.research.v1 import models_pb2 as research
43
+ from arvo.services.v1 import research_pb2_grpc as rpc
44
+
45
+
46
+ class _Pb:
47
+ """The shapes this client sends, from whichever package defines them."""
48
+
49
+ Empty = common.Empty
50
+ AttachRequest = research.AttachRequest
51
+ FindingId = research.FindingId
52
+ LedgerTrade = research.LedgerTrade
53
+ Point = research.Point
54
+ ReportRequest = research.ReportRequest
55
+ RunRequest = research.RunRequest
56
+
57
+
58
+ pb = _Pb()
59
+
60
+ __all__ = [
61
+ "Advice",
62
+ "ArvoError",
63
+ "Attachment",
64
+ "Engine",
65
+ "Finding",
66
+ "FindingSummary",
67
+ "Instrument",
68
+ "Strategy",
69
+ "connect",
70
+ "default_root",
71
+ ]
72
+
73
+ DAILY = "1day"
74
+ """The interval name of daily bars, as the engine spells it."""
75
+
76
+ _SAFE_INSTRUMENT = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]*$")
77
+
78
+
79
+ class ArvoError(RuntimeError):
80
+ """A call the engine refused or could not complete, with its reason."""
81
+
82
+ def __init__(self, message: str, code: str = "") -> None:
83
+ super().__init__(message)
84
+ self.code = code
85
+
86
+
87
+ @dataclass(frozen=True)
88
+ class Strategy:
89
+ name: str
90
+ label: str
91
+ interval: str
92
+ premise: str
93
+ ranks_a_set: bool
94
+
95
+
96
+ @dataclass(frozen=True)
97
+ class Instrument:
98
+ id: str
99
+ interval: str
100
+ start: str
101
+ end: str
102
+
103
+
104
+ @dataclass(frozen=True)
105
+ class FindingSummary:
106
+ id: str
107
+ kind: str
108
+ subject: str
109
+ verdict: str
110
+ recorded_at: str
111
+ author: str
112
+ """The agent or script that ran it; empty for a person."""
113
+ strategy: str = ""
114
+ """Which rule or ruleset ran, by the name ``strategies()`` shows."""
115
+ code_commit: str = ""
116
+ """The engine build that produced it: a git commit, ``-dirty`` when the
117
+ tree had uncommitted changes, ``unknown`` outside a checkout, empty for a
118
+ finding recorded before builds were stamped."""
119
+ ruleset_hash: str = ""
120
+ """The ruleset document's hash at run time, when the strategy was a
121
+ ruleset rather than a shipped rule. Two findings with different hashes
122
+ measured different rules, whatever the ruleset is called."""
123
+
124
+
125
+ @dataclass(frozen=True)
126
+ class Advice:
127
+ severity: str
128
+ finding: str
129
+ action: str
130
+ evidence: str
131
+
132
+
133
+ @dataclass(frozen=True)
134
+ class Attachment:
135
+ """One file kept with a finding: bytes stored once by content hash."""
136
+
137
+ name: str
138
+ media_type: str
139
+ hash: str
140
+ bytes: int
141
+ added_at: str
142
+
143
+
144
+ @dataclass(frozen=True)
145
+ class Finding:
146
+ id: str
147
+ kind: str
148
+ subject: str
149
+ verdict: str
150
+ recorded_at: str
151
+ author: str
152
+ read_this_first: str
153
+ """How far the verdict may be read. Read before any number."""
154
+ reasons: list[str]
155
+ advice: list[Advice]
156
+ strategy: str = ""
157
+ code_commit: str = ""
158
+ ruleset_hash: str = ""
159
+ detail: dict[str, Any] = field(default_factory=dict)
160
+ """The numbers, which differ by kind: ``search`` and ``out_of_sample`` for
161
+ a study, ``combined`` for a walk-forward, ``pooled`` for a panel."""
162
+ attachments: list[Attachment] = field(default_factory=list)
163
+ """Files kept with it, in the order they were attached."""
164
+
165
+
166
+ def default_root() -> Path:
167
+ """The Arvo app data directory, ``%APPDATA%/com.arvo.desktop``."""
168
+ appdata = os.environ.get("APPDATA")
169
+ if not appdata:
170
+ raise ArvoError("APPDATA is not set; pass the Arvo app data directory to connect()")
171
+ return Path(appdata) / "com.arvo.desktop"
172
+
173
+
174
+ def connect(root: str | os.PathLike[str] | None = None) -> Engine:
175
+ """Connects to the engine running for this app data directory.
176
+
177
+ Reads ``engine.json``, which the engine writes when it starts. Raises
178
+ :class:`ArvoError` when no engine has written one.
179
+ """
180
+ base = Path(root) if root is not None else default_root()
181
+ try:
182
+ found = json.loads((base / "engine.json").read_text(encoding="utf-8"))
183
+ except FileNotFoundError:
184
+ raise ArvoError(
185
+ f"no engine.json in {base}; start arvo-engine first"
186
+ ) from None
187
+ return Engine(base, found["address"], found["token"])
188
+
189
+
190
+ class Engine:
191
+ """One engine's research API, and read-only access to its bar library."""
192
+
193
+ def __init__(self, root: Path, address: str, token: str) -> None:
194
+ self.root = root
195
+ # Room for a figure or a trades table attached to a finding; the
196
+ # engine accepts the same.
197
+ limit = 64 << 20
198
+ self._channel = grpc.insecure_channel(
199
+ address,
200
+ options=[("grpc.max_send_message_length", limit), ("grpc.max_receive_message_length", limit)],
201
+ )
202
+ self._stub = rpc.ResearchStub(self._channel)
203
+ self._metadata = (("authorization", f"Bearer {token}"),)
204
+
205
+ def close(self) -> None:
206
+ self._channel.close()
207
+
208
+ def __enter__(self) -> Engine:
209
+ return self
210
+
211
+ def __exit__(self, *_: object) -> None:
212
+ self.close()
213
+
214
+ def _call(self, method: Any, request: Any) -> Any:
215
+ try:
216
+ return method(request, metadata=self._metadata)
217
+ except grpc.RpcError as err:
218
+ code = err.code().name if hasattr(err, "code") else ""
219
+ details = err.details() if hasattr(err, "details") else str(err)
220
+ raise ArvoError(details or code, code) from None
221
+
222
+ def strategies(self) -> list[Strategy]:
223
+ """The rules Arvo can test."""
224
+ reply = self._call(self._stub.ListStrategies, pb.Empty())
225
+ return [
226
+ Strategy(s.name, s.label, s.interval, s.premise, s.ranks_a_set)
227
+ for s in reply.strategies
228
+ ]
229
+
230
+ def instruments(self) -> list[Instrument]:
231
+ """Instruments with bars in the library, by interval and date range."""
232
+ reply = self._call(self._stub.ListInstruments, pb.Empty())
233
+ return [Instrument(i.id, i.interval, getattr(i, "from"), i.to) for i in reply.instruments]
234
+
235
+ def findings(self) -> list[FindingSummary]:
236
+ """Every finding in research memory."""
237
+ reply = self._call(self._stub.ListFindings, pb.Empty())
238
+ return [
239
+ FindingSummary(
240
+ f.id, f.kind, f.subject, f.verdict, f.recorded_at, f.author,
241
+ f.strategy, f.code_commit, f.ruleset_hash,
242
+ )
243
+ for f in reply.findings
244
+ ]
245
+
246
+ def finding(self, id: str) -> Finding:
247
+ """One finding: verdict, how far to read it, reasons, advice, numbers."""
248
+ return _finding(self._call(self._stub.OpenFinding, pb.FindingId(id=id)))
249
+
250
+ def run_study(self, instrument: str, strategy: str, *, author: str) -> Finding:
251
+ """Searches ``strategy``'s grid on ``instrument``, chooses in-sample and
252
+ judges out-of-sample. Saved as ``author``'s finding and deflated against
253
+ everything ``author`` has run."""
254
+ request = pb.RunRequest(instrument=instrument, strategy=strategy, author=author, origin=_caller())
255
+ return _finding(self._call(self._stub.RunStudy, request))
256
+
257
+ def run_walk_forward(self, instrument: str, strategy: str, *, author: str) -> Finding:
258
+ """Re-selects on a rolling schedule across the whole history and judges
259
+ the stitched out-of-sample record. Slower than :meth:`run_study`."""
260
+ request = pb.RunRequest(instrument=instrument, strategy=strategy, author=author, origin=_caller())
261
+ return _finding(self._call(self._stub.RunWalkForward, request))
262
+
263
+ def record(
264
+ self,
265
+ *,
266
+ author: str,
267
+ hypothesis: str,
268
+ claim: str,
269
+ instrument: str,
270
+ window: tuple[str, str],
271
+ interval: str,
272
+ dataset: tuple[str, str],
273
+ strategy: str,
274
+ engine: str,
275
+ curve: Sequence[tuple[Any, float]],
276
+ ledger: Sequence[dict[str, Any]] = (),
277
+ benchmark_curve: Sequence[tuple[Any, float]] | None = None,
278
+ params: dict[str, float] | None = None,
279
+ costs_bps: tuple[float, float] = (0.0, 0.0),
280
+ per_fill: float = 0.0,
281
+ stop_atr_multiple: float | None = None,
282
+ starting_cash: float = 0.0,
283
+ adjustment: str = "split",
284
+ trials: int | None = None,
285
+ ) -> Finding:
286
+ """Records a result this script computed itself, judged by Arvo.
287
+
288
+ What a run produces, before anyone concludes anything from it: the
289
+ experiment as data, the strategy's equity ``curve`` and trade
290
+ ``ledger``, a ``benchmark_curve`` when there is one, and how many
291
+ ``trials`` the whole search has run. Arvo computes the verdict from
292
+ that with the criteria a study uses. There is no argument for a
293
+ verdict, a metric or a p-value, on purpose.
294
+
295
+ ``curve`` and ``benchmark_curve`` are ``(when, equity)`` pairs, where
296
+ ``when`` is a ``datetime``, a ``date`` or a string like
297
+ ``2026-01-31T09:30:00``. ``ledger`` rows are dicts with ``opened``,
298
+ ``closed`` (omit while open), ``direction`` (``long``/``short``),
299
+ ``quantity``, ``entry``, ``exit``, ``pnl``, ``commission`` and
300
+ ``exit_reason`` (``signal``, ``stop``, ``halted``, ``expired``,
301
+ ``still_open``), and optionally what the rule saw when it entered:
302
+ ``rule`` (the condition, in words), ``signal`` (the value it was
303
+ judged on), ``regime`` and ``asked`` (the quantity before the gate). ``dataset`` is ``(id, version)``, the version being
304
+ a content hash of what the engine read. Without a benchmark the
305
+ finding is Inconclusive: a return with nothing to beat is not a
306
+ result.
307
+ """
308
+ trades = [
309
+ pb.LedgerTrade(
310
+ instrument=str(row.get("instrument", "")),
311
+ opened=_when(row["opened"]),
312
+ closed=_when(row["closed"]) if row.get("closed") else "",
313
+ direction=str(row.get("direction", "long")),
314
+ quantity=float(row.get("quantity", 0.0)),
315
+ entry=float(row.get("entry", 0.0)),
316
+ pnl=float(row.get("pnl", 0.0)),
317
+ commission=float(row.get("commission", 0.0)),
318
+ exit_reason=str(row.get("exit_reason", "signal")),
319
+ **({"exit": float(row["exit"])} if row.get("exit") is not None else {}),
320
+ **({"rule": str(row["rule"])} if row.get("rule") is not None else {}),
321
+ **({"signal": float(row["signal"])} if row.get("signal") is not None else {}),
322
+ **({"regime": str(row["regime"])} if row.get("regime") is not None else {}),
323
+ **({"asked": float(row["asked"])} if row.get("asked") is not None else {}),
324
+ )
325
+ for row in ledger
326
+ ]
327
+ request = pb.ReportRequest(
328
+ author=author,
329
+ origin=_caller(),
330
+ hypothesis_id=hypothesis,
331
+ claim=claim,
332
+ instrument=instrument,
333
+ interval=interval,
334
+ dataset_id=dataset[0],
335
+ dataset_version=dataset[1],
336
+ adjustment=adjustment,
337
+ strategy=strategy,
338
+ params=dict(params or {}),
339
+ commission_bps=costs_bps[0],
340
+ slippage_bps=costs_bps[1],
341
+ per_fill=per_fill,
342
+ starting_cash=starting_cash,
343
+ engine=engine,
344
+ strategy_curve=[pb.Point(at=_when(at), equity=float(equity)) for at, equity in curve],
345
+ ledger=trades,
346
+ benchmark_curve=[pb.Point(at=_when(at), equity=float(equity)) for at, equity in (benchmark_curve or [])],
347
+ **({"trials": int(trials)} if trials is not None else {}),
348
+ **({"stop_atr_multiple": float(stop_atr_multiple)} if stop_atr_multiple is not None else {}),
349
+ )
350
+ # `from` is a keyword in Python; the field is set by name.
351
+ setattr(request, "from", window[0])
352
+ request.to = window[1]
353
+ return _finding(self._call(self._stub.RecordFinding, request))
354
+
355
+ def attach(
356
+ self,
357
+ finding_id: str,
358
+ path: str | os.PathLike[str] | None = None,
359
+ *,
360
+ data: bytes | None = None,
361
+ name: str | None = None,
362
+ media_type: str | None = None,
363
+ ) -> list[Attachment]:
364
+ """Keeps a file with a finding: a report, a figure, the trades.
365
+
366
+ Give a ``path`` to read, or ``data`` with a ``name``. The bytes are
367
+ stored once by content hash and the finding lists them; attaching the
368
+ same file under the same name again changes nothing. Returns every
369
+ attachment the finding now has. ``media_type`` is guessed from the
370
+ name when not given.
371
+ """
372
+ if path is not None:
373
+ file = Path(path)
374
+ payload = file.read_bytes()
375
+ name = name or file.name
376
+ elif data is not None:
377
+ payload = data
378
+ else:
379
+ raise ArvoError("attach needs a path or data")
380
+ if not name:
381
+ raise ArvoError("attach needs a name for the file")
382
+ if media_type is None:
383
+ media_type = _MEDIA_TYPES.get(Path(name).suffix.lower(), "application/octet-stream")
384
+ request = pb.AttachRequest(finding_id=finding_id, name=name, media_type=media_type, data=payload)
385
+ reply = self._call(self._stub.AttachFile, request)
386
+ return [_attachment(a) for a in reply.attachments]
387
+
388
+ def bars(self, instrument: str, interval: str = DAILY) -> Any:
389
+ """The library's bars for ``instrument`` as a pandas DataFrame indexed
390
+ by time, read straight from the engine's files.
391
+
392
+ Read-only. Stocks and funds only; an option contract is filed under its
393
+ underlying and is not read here.
394
+ """
395
+ import pandas as pd
396
+
397
+ if not _SAFE_INSTRUMENT.fullmatch(instrument) or ".." in instrument:
398
+ raise ArvoError(f"{instrument!r} is not an instrument id")
399
+ if not re.fullmatch(r"[0-9]+[a-z]+", interval):
400
+ raise ArvoError(f"{interval!r} is not an interval, e.g. '1day' or '5minute'")
401
+ data = self.root / "data"
402
+ folder = data if interval == DAILY else data / interval
403
+ path = folder / f"{instrument}.csv"
404
+ if not path.is_file():
405
+ raise ArvoError(f"no {interval} bars for {instrument} in {folder}")
406
+ frame = pd.read_csv(path, parse_dates=["date"], index_col="date")
407
+ return frame
408
+
409
+
410
+ def _caller() -> str:
411
+ """Where the script asked for a run, as ``path:line``: the frame above the
412
+ client's own method. The workbench shows the finding's caveats against
413
+ that line. Empty when there is no such frame."""
414
+ import inspect
415
+
416
+ frames = inspect.stack()
417
+ if len(frames) < 3:
418
+ return ""
419
+ caller = frames[2]
420
+ return f"{os.path.abspath(caller.filename)}:{caller.lineno}"
421
+
422
+
423
+ def _when(value: Any) -> str:
424
+ """A point in time as the engine reads it: ``2026-01-31T09:30:00``, or a
425
+ date. A ``datetime``, a ``date``, a pandas ``Timestamp`` or a string."""
426
+ if isinstance(value, str):
427
+ return value
428
+ if hasattr(value, "strftime"):
429
+ if hasattr(value, "hour"):
430
+ return value.strftime("%Y-%m-%dT%H:%M:%S")
431
+ return value.strftime("%Y-%m-%d")
432
+ return str(value)
433
+
434
+
435
+ _MEDIA_TYPES = {
436
+ ".md": "text/markdown",
437
+ ".csv": "text/csv",
438
+ ".json": "application/json",
439
+ ".png": "image/png",
440
+ ".svg": "image/svg+xml",
441
+ ".txt": "text/plain",
442
+ ".html": "text/html",
443
+ ".pdf": "application/pdf",
444
+ }
445
+
446
+
447
+ def _attachment(kept: Any) -> Attachment:
448
+ return Attachment(kept.name, kept.media_type, kept.hash, int(kept.bytes), kept.added_at)
449
+
450
+
451
+ def _finding(reply: Any) -> Finding:
452
+ summary = reply.summary
453
+ return Finding(
454
+ id=summary.id,
455
+ kind=summary.kind,
456
+ subject=summary.subject,
457
+ verdict=summary.verdict,
458
+ recorded_at=summary.recorded_at,
459
+ author=summary.author,
460
+ read_this_first=reply.read_this_first,
461
+ reasons=list(reply.reasons),
462
+ advice=[Advice(a.severity, a.finding, a.action, a.evidence) for a in reply.advice],
463
+ strategy=summary.strategy,
464
+ code_commit=summary.code_commit,
465
+ ruleset_hash=summary.ruleset_hash,
466
+ detail=json.loads(reply.detail_json) if reply.detail_json else {},
467
+ attachments=[_attachment(a) for a in reply.attachments],
468
+ )
File without changes
File without changes
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: arvo/common/v1/models.proto
5
+ # Protobuf Python Version: 6.31.1
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 31,
16
+ 1,
17
+ '',
18
+ 'arvo/common/v1/models.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+
26
+
27
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x61rvo/common/v1/models.proto\x12\x0e\x61rvo.common.v1\"\x07\n\x05\x45mpty\"\x1c\n\x0c\x45xportedPath\x12\x0c\n\x04path\x18\x01 \x01(\tb\x06proto3')
28
+
29
+ _globals = globals()
30
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
31
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'arvo.common.v1.models_pb2', _globals)
32
+ if not _descriptor._USE_C_DESCRIPTORS:
33
+ DESCRIPTOR._loaded_options = None
34
+ _globals['_EMPTY']._serialized_start=47
35
+ _globals['_EMPTY']._serialized_end=54
36
+ _globals['_EXPORTEDPATH']._serialized_start=56
37
+ _globals['_EXPORTEDPATH']._serialized_end=84
38
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,24 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+ import grpc
4
+ import warnings
5
+
6
+
7
+ GRPC_GENERATED_VERSION = '1.75.1'
8
+ GRPC_VERSION = grpc.__version__
9
+ _version_not_supported = False
10
+
11
+ try:
12
+ from grpc._utilities import first_version_is_lower
13
+ _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
14
+ except ImportError:
15
+ _version_not_supported = True
16
+
17
+ if _version_not_supported:
18
+ raise RuntimeError(
19
+ f'The grpc package installed is at version {GRPC_VERSION},'
20
+ + f' but the generated code in arvo/common/v1/models_pb2_grpc.py depends on'
21
+ + f' grpcio>={GRPC_GENERATED_VERSION}.'
22
+ + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
23
+ + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
24
+ )
File without changes
File without changes
@@ -0,0 +1,46 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: arvo/market/v1/models.proto
5
+ # Protobuf Python Version: 6.31.1
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 31,
16
+ 1,
17
+ '',
18
+ 'arvo/market/v1/models.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+
26
+
27
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x61rvo/market/v1/models.proto\x12\x0e\x61rvo.market.v1\"D\n\nInstrument\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08interval\x18\x02 \x01(\t\x12\x0c\n\x04\x66rom\x18\x03 \x01(\t\x12\n\n\x02to\x18\x04 \x01(\t\">\n\x0bInstruments\x12/\n\x0binstruments\x18\x01 \x03(\x0b\x32\x1a.arvo.market.v1.Instrument\"[\n\tQuoteTick\x12\x0e\n\x06symbol\x18\x01 \x01(\t\x12\r\n\x05price\x18\x02 \x01(\x01\x12\x13\n\x06\x63hange\x18\x03 \x01(\x01H\x00\x88\x01\x01\x12\x0f\n\x07regular\x18\x04 \x01(\x08\x42\t\n\x07_change\"A\n\x10InstrumentSearch\x12\r\n\x05query\x18\x01 \x01(\t\x12\x13\n\x06source\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\t\n\x07_source\"p\n\x0c\x46\x65tchRequest\x12\x12\n\ninstrument\x18\x01 \x01(\t\x12\x10\n\x08interval\x18\x02 \x01(\t\x12\x11\n\x04\x64\x61ys\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x01\x88\x01\x01\x42\x07\n\x05_daysB\t\n\x07_source\"\x90\x01\n\x0e\x43ompareRequest\x12\x12\n\ninstrument\x18\x01 \x01(\t\x12\x10\n\x08interval\x18\x02 \x01(\t\x12\x12\n\x05\x66irst\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06second\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04\x64\x61ys\x18\x05 \x01(\rH\x02\x88\x01\x01\x42\x08\n\x06_firstB\t\n\x07_secondB\x07\n\x05_daysb\x06proto3')
28
+
29
+ _globals = globals()
30
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
31
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'arvo.market.v1.models_pb2', _globals)
32
+ if not _descriptor._USE_C_DESCRIPTORS:
33
+ DESCRIPTOR._loaded_options = None
34
+ _globals['_INSTRUMENT']._serialized_start=47
35
+ _globals['_INSTRUMENT']._serialized_end=115
36
+ _globals['_INSTRUMENTS']._serialized_start=117
37
+ _globals['_INSTRUMENTS']._serialized_end=179
38
+ _globals['_QUOTETICK']._serialized_start=181
39
+ _globals['_QUOTETICK']._serialized_end=272
40
+ _globals['_INSTRUMENTSEARCH']._serialized_start=274
41
+ _globals['_INSTRUMENTSEARCH']._serialized_end=339
42
+ _globals['_FETCHREQUEST']._serialized_start=341
43
+ _globals['_FETCHREQUEST']._serialized_end=453
44
+ _globals['_COMPAREREQUEST']._serialized_start=456
45
+ _globals['_COMPAREREQUEST']._serialized_end=600
46
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,24 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+ import grpc
4
+ import warnings
5
+
6
+
7
+ GRPC_GENERATED_VERSION = '1.75.1'
8
+ GRPC_VERSION = grpc.__version__
9
+ _version_not_supported = False
10
+
11
+ try:
12
+ from grpc._utilities import first_version_is_lower
13
+ _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
14
+ except ImportError:
15
+ _version_not_supported = True
16
+
17
+ if _version_not_supported:
18
+ raise RuntimeError(
19
+ f'The grpc package installed is at version {GRPC_VERSION},'
20
+ + f' but the generated code in arvo/market/v1/models_pb2_grpc.py depends on'
21
+ + f' grpcio>={GRPC_GENERATED_VERSION}.'
22
+ + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
23
+ + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
24
+ )
@@ -0,0 +1,56 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: arvo/market/v1/views.proto
5
+ # Protobuf Python Version: 6.31.1
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 31,
16
+ 1,
17
+ '',
18
+ 'arvo/market/v1/views.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+
26
+
27
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1a\x61rvo/market/v1/views.proto\x12\x0e\x61rvo.market.v1\"Y\n\x0f\x44\x61taFindingView\x12\x10\n\x08severity\x18\x01 \x01(\t\x12\x0c\n\x04kind\x18\x02 \x01(\t\x12\x0f\n\x02\x61t\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x0e\n\x06\x64\x65tail\x18\x04 \x01(\tB\x05\n\x03_at\"Y\n\x0f\x44\x61taLibraryView\x12\x11\n\tdirectory\x18\x01 \x01(\t\x12\x33\n\x0binstruments\x18\x02 \x03(\x0b\x32\x1e.arvo.market.v1.InstrumentView\"\xac\x02\n\tFetchView\x12\x12\n\ninstrument\x18\x01 \x01(\t\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x10\n\x08interval\x18\x03 \x01(\t\x12\x0c\n\x04\x62\x61rs\x18\x04 \x01(\r\x12\x14\n\x0cinterpolated\x18\x05 \x01(\r\x12\x11\n\x04\x66rom\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x02to\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x36\n\rdata_findings\x18\x08 \x03(\x0b\x32\x1f.arvo.market.v1.DataFindingView\x12\x15\n\x08revision\x18\t \x01(\tH\x02\x88\x01\x01\x12\x0f\n\x07revised\x18\n \x01(\x08\x12\x16\n\tdividends\x18\x0b \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_fromB\x05\n\x03_toB\x0b\n\t_revisionB\x0c\n\n_dividends\"\x88\x01\n\x0eInstrumentView\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04\x66rom\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x02to\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x0c\n\x04\x62\x61rs\x18\x04 \x01(\r\x12\x18\n\x0b\x66ingerprint\x18\x05 \x01(\tH\x02\x88\x01\x01\x42\x07\n\x05_fromB\x05\n\x03_toB\x0e\n\x0c_fingerprint\"\x89\x01\n\tMatchView\x12\x12\n\ninstrument\x18\x01 \x01(\t\x12\x0e\n\x06symbol\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x12\n\x05price\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x63hange\x18\x05 \x01(\x01H\x01\x88\x01\x01\x12\x0c\n\x04held\x18\x06 \x01(\x08\x42\x08\n\x06_priceB\t\n\x07_change\"{\n\tQuoteView\x12\x12\n\ninstrument\x18\x01 \x01(\t\x12\x0e\n\x06symbol\x18\x02 \x01(\t\x12\x12\n\x05price\x18\x03 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x63hange\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x0c\n\x04held\x18\x05 \x01(\x08\x42\x08\n\x06_priceB\t\n\x07_change\"\x8c\x02\n\x14SourceComparisonView\x12\x0e\n\x06symbol\x18\x01 \x01(\t\x12\x10\n\x08interval\x18\x02 \x01(\t\x12\r\n\x05\x66irst\x18\x03 \x01(\t\x12\x0e\n\x06second\x18\x04 \x01(\t\x12\x12\n\nfirst_bars\x18\x05 \x01(\r\x12\x13\n\x0bsecond_bars\x18\x06 \x01(\r\x12\x0e\n\x06shared\x18\x07 \x01(\r\x12\x12\n\nonly_first\x18\x08 \x01(\r\x12\x13\n\x0bonly_second\x18\t \x01(\r\x12\x0f\n\x07summary\x18\n \x01(\t\x12\x10\n\x08\x64iverged\x18\x0b \x01(\x08\x12\x1b\n\x0e\x62\x61sis_mismatch\x18\x0c \x01(\tH\x00\x88\x01\x01\x42\x11\n\x0f_basis_mismatch\"t\n\nSourceView\x12\n\n\x02id\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\r\n\x05venue\x18\x03 \x01(\t\x12\x11\n\tconnected\x18\x04 \x01(\x08\x12\x15\n\rneeds_sign_in\x18\x05 \x01(\x08\x12\x12\n\nneeds_keys\x18\x06 \x01(\x08\":\n\x0bSourcesView\x12+\n\x07sources\x18\x01 \x03(\x0b\x32\x1a.arvo.market.v1.SourceView\"9\n\x0bMatchesView\x12*\n\x07matches\x18\x01 \x03(\x0b\x32\x19.arvo.market.v1.MatchView\"8\n\rWatchlistView\x12\'\n\x04rows\x18\x01 \x03(\x0b\x32\x19.arvo.market.v1.QuoteViewb\x06proto3')
28
+
29
+ _globals = globals()
30
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
31
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'arvo.market.v1.views_pb2', _globals)
32
+ if not _descriptor._USE_C_DESCRIPTORS:
33
+ DESCRIPTOR._loaded_options = None
34
+ _globals['_DATAFINDINGVIEW']._serialized_start=46
35
+ _globals['_DATAFINDINGVIEW']._serialized_end=135
36
+ _globals['_DATALIBRARYVIEW']._serialized_start=137
37
+ _globals['_DATALIBRARYVIEW']._serialized_end=226
38
+ _globals['_FETCHVIEW']._serialized_start=229
39
+ _globals['_FETCHVIEW']._serialized_end=529
40
+ _globals['_INSTRUMENTVIEW']._serialized_start=532
41
+ _globals['_INSTRUMENTVIEW']._serialized_end=668
42
+ _globals['_MATCHVIEW']._serialized_start=671
43
+ _globals['_MATCHVIEW']._serialized_end=808
44
+ _globals['_QUOTEVIEW']._serialized_start=810
45
+ _globals['_QUOTEVIEW']._serialized_end=933
46
+ _globals['_SOURCECOMPARISONVIEW']._serialized_start=936
47
+ _globals['_SOURCECOMPARISONVIEW']._serialized_end=1204
48
+ _globals['_SOURCEVIEW']._serialized_start=1206
49
+ _globals['_SOURCEVIEW']._serialized_end=1322
50
+ _globals['_SOURCESVIEW']._serialized_start=1324
51
+ _globals['_SOURCESVIEW']._serialized_end=1382
52
+ _globals['_MATCHESVIEW']._serialized_start=1384
53
+ _globals['_MATCHESVIEW']._serialized_end=1441
54
+ _globals['_WATCHLISTVIEW']._serialized_start=1443
55
+ _globals['_WATCHLISTVIEW']._serialized_end=1499
56
+ # @@protoc_insertion_point(module_scope)