openscript 0.4.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.
- openscript/__init__.py +40 -0
- openscript/__main__.py +62 -0
- openscript/accounting/__init__.py +74 -0
- openscript/accounting/analysis.py +174 -0
- openscript/accounting/charges.py +397 -0
- openscript/accounting/equity.py +234 -0
- openscript/accounting/report.py +82 -0
- openscript/accounting/shapes.py +74 -0
- openscript/accounting/statistics.py +300 -0
- openscript/accounting/trades.py +294 -0
- openscript/adapter/__init__.py +32 -0
- openscript/adapter/answers.py +215 -0
- openscript/adapter/channels.py +137 -0
- openscript/adapter/expectations.py +67 -0
- openscript/adapter/facts.py +127 -0
- openscript/adapter/matching.py +257 -0
- openscript/adapter/ordering.py +187 -0
- openscript/adapter/page.py +130 -0
- openscript/adapter/reading.py +357 -0
- openscript/adapter/reporting.py +244 -0
- openscript/adapter/running.py +449 -0
- openscript/adapter/serving.py +229 -0
- openscript/adapter/sessions.py +168 -0
- openscript/adapter/spellings.py +184 -0
- openscript/bars.py +157 -0
- openscript/budget.py +342 -0
- openscript/canonical.py +192 -0
- openscript/civil.py +196 -0
- openscript/contracts.py +165 -0
- openscript/dates.py +302 -0
- openscript/diagnostics.py +104 -0
- openscript/hours.py +165 -0
- openscript/inputs.py +239 -0
- openscript/intervals.py +60 -0
- openscript/library/__init__.py +76 -0
- openscript/library/arithmetic.py +128 -0
- openscript/library/averages.py +133 -0
- openscript/library/bars.py +60 -0
- openscript/library/bookkeeping.py +166 -0
- openscript/library/code_points.py +85 -0
- openscript/library/colour.py +202 -0
- openscript/library/composites.py +208 -0
- openscript/library/counting.py +218 -0
- openscript/library/deviation.py +155 -0
- openscript/library/elementary.py +206 -0
- openscript/library/extremes.py +122 -0
- openscript/library/flows.py +220 -0
- openscript/library/momentum.py +203 -0
- openscript/library/number_text.py +223 -0
- openscript/library/prices.py +36 -0
- openscript/library/ranges.py +105 -0
- openscript/library/rounding.py +123 -0
- openscript/library/series.py +213 -0
- openscript/library/stateful.py +442 -0
- openscript/library/stateless.py +261 -0
- openscript/library/strength.py +180 -0
- openscript/library/strings.py +228 -0
- openscript/library/trend.py +260 -0
- openscript/library/values.py +91 -0
- openscript/logbook.py +119 -0
- openscript/machine.py +499 -0
- openscript/memory.py +204 -0
- openscript/opcodes.py +166 -0
- openscript/program.py +146 -0
- openscript/run.py +368 -0
- openscript/strategy/__init__.py +78 -0
- openscript/strategy/calls.py +201 -0
- openscript/strategy/closable.py +182 -0
- openscript/strategy/fills.py +131 -0
- openscript/strategy/holdings.py +277 -0
- openscript/strategy/intents.py +162 -0
- openscript/strategy/ledger.py +270 -0
- openscript/strategy/placing.py +206 -0
- openscript/strategy/positions.py +124 -0
- openscript/strategy/refusals.py +293 -0
- openscript/strategy/rows.py +219 -0
- openscript/strategy/sizing.py +229 -0
- openscript/strategy/statuses.py +65 -0
- openscript/surface/__init__.py +115 -0
- openscript/surface/bands.py +103 -0
- openscript/surface/levels.py +44 -0
- openscript/surface/marks.py +52 -0
- openscript/surface/paints.py +58 -0
- openscript/surface/plots.py +44 -0
- openscript/surface/published.py +119 -0
- openscript/values.py +210 -0
- openscript/verify.py +301 -0
- openscript/verify_code.py +290 -0
- openscript/verify_requests.py +271 -0
- openscript/verify_shape.py +162 -0
- openscript/verify_tables.py +256 -0
- openscript/version.py +39 -0
- openscript/zones.py +118 -0
- openscript-0.4.0.dist-info/METADATA +82 -0
- openscript-0.4.0.dist-info/RECORD +97 -0
- openscript-0.4.0.dist-info/WHEEL +5 -0
- openscript-0.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Absence, and the two rules every number this library returns has passed.
|
|
2
|
+
|
|
3
|
+
`compiled-program.md` section 3.1 states both, and they are here rather than in
|
|
4
|
+
each function because a rule written out thirty times is a rule with thirty
|
|
5
|
+
places to get it wrong:
|
|
6
|
+
|
|
7
|
+
- **A number is always finite.** Any result that is not is absent, checked after
|
|
8
|
+
each individual operation rather than at the end of an expression, so that two
|
|
9
|
+
engines cannot differ over where one of them happened to round.
|
|
10
|
+
- **Negative zero is normalised to positive zero.** Nothing in the language can
|
|
11
|
+
observe the sign of a zero, and a rule that could spell one would let it reach
|
|
12
|
+
a string through ``text`` and differ between two engines for no reason a
|
|
13
|
+
reader could act on.
|
|
14
|
+
|
|
15
|
+
Absence is ``None`` and never a not-a-number. `compiled-program.md` section 3.4
|
|
16
|
+
gives the reason: a not-a-number propagates through arithmetic by accident,
|
|
17
|
+
which is the right answer for some operators and the wrong one for others, and
|
|
18
|
+
it would turn an absence test into a floating point comparison.
|
|
19
|
+
|
|
20
|
+
**Nothing here raises.** A wrong argument is a diagnostic the checker or the
|
|
21
|
+
interpreter produces, with a code and a span, before a call reaches a function
|
|
22
|
+
in this package (`stdlib.md` section 2.4). The backstops below return absence
|
|
23
|
+
instead, because a wrong number drawn on a chart is worse than a gap.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import math
|
|
27
|
+
|
|
28
|
+
# A value the machine holds, minus the reference: the interpreter owns the heap
|
|
29
|
+
# and the object that lives in it, so a type written here would be that type
|
|
30
|
+
# stated twice. What this package promises about one is narrower than the tag
|
|
31
|
+
# list: a function takes what its entry in `stdlib.md` says it takes.
|
|
32
|
+
Value = float | bool | str | None
|
|
33
|
+
|
|
34
|
+
# Absence, written `none` in a script.
|
|
35
|
+
ABSENT: None = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def is_number(value: Value) -> bool:
|
|
39
|
+
"""Whether a value is a number, which a bool is not.
|
|
40
|
+
|
|
41
|
+
``isinstance(True, float)`` is false in this language and true in several
|
|
42
|
+
others, but a bool is an integer here, so ``value == 1`` is true of ``True``
|
|
43
|
+
and a numeric guard written that way would let a bool through into
|
|
44
|
+
arithmetic. The tag is asked for instead.
|
|
45
|
+
"""
|
|
46
|
+
return isinstance(value, (int, float)) and not isinstance(value, bool)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def result(x: float) -> Value:
|
|
50
|
+
"""A computed number, made safe to return: finite, with one zero."""
|
|
51
|
+
if not math.isfinite(x):
|
|
52
|
+
return ABSENT
|
|
53
|
+
return 0.0 if x == 0 else float(x)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def number(value: Value) -> float | None:
|
|
57
|
+
"""A numeric argument as a number, or absence for anything that is not one.
|
|
58
|
+
|
|
59
|
+
The absence covers two cases that are one case here: the argument the script
|
|
60
|
+
wrote was absent on this bar, and the argument was of another type, which is
|
|
61
|
+
a program the checker should not have accepted. Both are absence rather than
|
|
62
|
+
a raise, per the module's opening note.
|
|
63
|
+
"""
|
|
64
|
+
return float(value) if is_number(value) else None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def whole(value: Value) -> int | None:
|
|
68
|
+
"""A count argument as a whole number of zero or more, or absence.
|
|
69
|
+
|
|
70
|
+
A digit count and a width are counts, not measurements: `stdlib.md` section
|
|
71
|
+
2.5 refuses a fractional one at compile time when it is a literal and at run
|
|
72
|
+
time when it is not, with a fix naming ``round``, because a length of 14.5 is
|
|
73
|
+
a bug in the script and rounding it on the script's behalf hides the bug.
|
|
74
|
+
This is the backstop under those diagnostics and not a second rule.
|
|
75
|
+
"""
|
|
76
|
+
x = number(value)
|
|
77
|
+
if x is None or not math.isfinite(x) or x != int(x) or x < 0:
|
|
78
|
+
return None
|
|
79
|
+
return int(x)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def floor_of(x: float) -> float:
|
|
83
|
+
"""``floor`` over binary64, including the values it has no whole number for.
|
|
84
|
+
|
|
85
|
+
The interpreter's own floor raises on an infinity rather than returning one,
|
|
86
|
+
and an infinity is exactly what the argument is when a division inside
|
|
87
|
+
``mod`` or a scaling inside a rounding overflows. Returning it unchanged
|
|
88
|
+
lets the arithmetic carry on and ``result`` turn the overflow into absence
|
|
89
|
+
at the end, which is where `compiled-program.md` section 3.1 puts it.
|
|
90
|
+
"""
|
|
91
|
+
return x if not math.isfinite(x) else float(math.floor(x))
|
openscript/logbook.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""The script's log: what ``print`` leaves behind, and what a host does with it.
|
|
2
|
+
|
|
3
|
+
`stdlib.md` 14.3 is the call. Three sentences of it are the whole of the
|
|
4
|
+
behaviour, and each one is a line of code here rather than a paragraph in a
|
|
5
|
+
document nobody runs:
|
|
6
|
+
|
|
7
|
+
- **``print`` writes to the per-script log, not to the chart.** It draws
|
|
8
|
+
nothing, lands in no contract field and answers nothing, so a case's numeric
|
|
9
|
+
output is identical with logging on and with logging off. That is not this
|
|
10
|
+
module's promise to keep: ``print`` carries an effect, so the machine pushes
|
|
11
|
+
absence and holds the record until step 9 (`compiled-program.md` 5.4), and the
|
|
12
|
+
columns are written at step 8, before any of this runs. What is kept here is a
|
|
13
|
+
test that would notice if it stopped being true.
|
|
14
|
+
- **Every entry carries the bar's time**, so a log line can be matched to a bar.
|
|
15
|
+
A bar index goes with it, because `conformance.md` section 7's example of a
|
|
16
|
+
log case is a line carrying one, and the index is what a runner compares
|
|
17
|
+
against an expected file without knowing the dataset's own clock.
|
|
18
|
+
- **It is rate limited by the host rather than by the language, and a host that
|
|
19
|
+
drops lines must say how many it dropped rather than truncating silently.** So
|
|
20
|
+
the limit arrives from the caller and is not a number this engine chose, and
|
|
21
|
+
the count of what it dropped is part of the answer rather than a log line
|
|
22
|
+
saying so, which would itself be a line a limit could drop.
|
|
23
|
+
|
|
24
|
+
**A moving bar writes nothing that is not decided.** Step 9 applies the records
|
|
25
|
+
of a bar this engine decided, and `stdlib.md` 14.3's rollback question is open in
|
|
26
|
+
`feature-matrix.md` for the case it does not cover: whether lines written during
|
|
27
|
+
a re-executed moving bar replace the previous execution's or are deferred like an
|
|
28
|
+
order. Nothing here answers it. The records this is handed are the ones step 9
|
|
29
|
+
applied, which is the half the pages do fix, and a caller replaying a moving bar
|
|
30
|
+
is handed the same answer twice rather than a reading this module invented.
|
|
31
|
+
|
|
32
|
+
**The shape of a line is this engine's and not a page's, and it is written down
|
|
33
|
+
once.** `conformance.md` section 4 says the ``log`` channel is an ordered list
|
|
34
|
+
whose elements are flat objects of named fields, and nothing anywhere names the
|
|
35
|
+
fields. The first engine hands the host the raw effect and builds no line at all,
|
|
36
|
+
so there is no second engine's spelling to agree with either. ``rows`` below is
|
|
37
|
+
therefore the one place a name is chosen, and the day a page fixes them it is the
|
|
38
|
+
one place that changes.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
from dataclasses import dataclass, field
|
|
42
|
+
from typing import Any, Dict, Iterable, List, Optional, Tuple
|
|
43
|
+
|
|
44
|
+
from .contracts import LibraryEntry
|
|
45
|
+
from .values import ABSENT
|
|
46
|
+
|
|
47
|
+
#: `stdlib.md` 14.3's one call, and the effect `compiled-program.md` 5.4 gives
|
|
48
|
+
#: it. One argument: the value. The extra argument of section 4.10 belongs to an
|
|
49
|
+
#: order call, whose defaults are absence itself, and this call has no default to
|
|
50
|
+
#: tell apart from a value that came out absent.
|
|
51
|
+
PRINT = "print"
|
|
52
|
+
|
|
53
|
+
LOG_ENTRIES: Dict[Tuple[str, int], LibraryEntry] = {
|
|
54
|
+
(PRINT, 1): LibraryEntry(PRINT, 1, False, "log")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(frozen=True)
|
|
59
|
+
class Line:
|
|
60
|
+
"""One entry of the log: the bar it was written on, and the value written."""
|
|
61
|
+
|
|
62
|
+
#: The index of the bar being executed, which is what a case compares.
|
|
63
|
+
bar: int
|
|
64
|
+
#: The bar's own open time, `host-interface.md` 3.1, in UTC milliseconds.
|
|
65
|
+
time: Any
|
|
66
|
+
#: Whatever the script passed, absence included. A script that printed a
|
|
67
|
+
#: value which was absent during warmup wrote an absent line, and an engine
|
|
68
|
+
#: that turned it into an empty string would hide the one thing the author is
|
|
69
|
+
#: looking at the log for.
|
|
70
|
+
value: Any = ABSENT
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class Logbook:
|
|
75
|
+
"""One run's log, with the ceiling the host sets on it.
|
|
76
|
+
|
|
77
|
+
``limit`` is the host's rate limit and ``None`` is a host that states none,
|
|
78
|
+
which is every conformance run: a case fixes its input in files and a limit
|
|
79
|
+
the suite did not state would be a limit one engine applied and another did
|
|
80
|
+
not. A host that does state one gets what it asked for and the count of what
|
|
81
|
+
that cost, which is 14.3's own requirement and the reason the count is a
|
|
82
|
+
field here rather than a line in the log.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
limit: Optional[int] = None
|
|
86
|
+
lines: List[Line] = field(default_factory=list)
|
|
87
|
+
dropped: int = 0
|
|
88
|
+
|
|
89
|
+
def write(self, effects: Iterable[Any], bar: int, time: Any) -> None:
|
|
90
|
+
"""The log records one execution left behind, in the order it made them.
|
|
91
|
+
|
|
92
|
+
The records are step 9's, so a call this is handed was made on a bar the
|
|
93
|
+
engine decided. Order is the bar's own and is never sorted: a script
|
|
94
|
+
printing a heading and then a row is a script whose two lines are only
|
|
95
|
+
useful in that order.
|
|
96
|
+
"""
|
|
97
|
+
for effect in effects:
|
|
98
|
+
if getattr(effect, "name", None) != PRINT:
|
|
99
|
+
continue
|
|
100
|
+
arguments = getattr(effect, "arguments", ())
|
|
101
|
+
self.add(bar, time, arguments[0] if arguments else ABSENT)
|
|
102
|
+
|
|
103
|
+
def add(self, bar: int, time: Any, value: Any) -> None:
|
|
104
|
+
"""One line, unless the host's limit has been reached and it is counted instead."""
|
|
105
|
+
if self.limit is not None and len(self.lines) >= self.limit:
|
|
106
|
+
self.dropped += 1
|
|
107
|
+
return
|
|
108
|
+
self.lines.append(Line(bar, time, value))
|
|
109
|
+
|
|
110
|
+
def rows(self) -> List[Dict[str, Any]]:
|
|
111
|
+
"""The lines as the flat objects section 4 compares a non columnar channel as.
|
|
112
|
+
|
|
113
|
+
The field names are this module's, for the reason the opening note gives:
|
|
114
|
+
no page fixes them. What is not this module's is the encoding of the
|
|
115
|
+
value, which section 4 already fixes for every channel and the adapter
|
|
116
|
+
already carries in one place, so a value goes out as it is held and is
|
|
117
|
+
spelled where every other spelling is decided.
|
|
118
|
+
"""
|
|
119
|
+
return [{"barIndex": one.bar, "time": one.time, "value": one.value} for one in self.lines]
|