PyDecisionGraph 0.3.2.post1__cp315-cp315-win_amd64.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.
- decision_graph/__infra__.pxd +1 -0
- decision_graph/__init__.pxd +1 -0
- decision_graph/__init__.py +39 -0
- decision_graph/decision_tree/__init__.pxd +2 -0
- decision_graph/decision_tree/__init__.py +85 -0
- decision_graph/decision_tree/bake/__infra__.pxd +502 -0
- decision_graph/decision_tree/bake/__init__.pxd +502 -0
- decision_graph/decision_tree/bake/__init__.py +29 -0
- decision_graph/decision_tree/bake/c_action.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_action.h +324 -0
- decision_graph/decision_tree/bake/c_action.pxd +54 -0
- decision_graph/decision_tree/bake/c_action.pyi +126 -0
- decision_graph/decision_tree/bake/c_action.pyx +65 -0
- decision_graph/decision_tree/bake/c_allocator_protocol.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_allocator_protocol.pxd +24 -0
- decision_graph/decision_tree/bake/c_allocator_protocol.pyi +61 -0
- decision_graph/decision_tree/bake/c_allocator_protocol.pyx +109 -0
- decision_graph/decision_tree/bake/c_bake.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_bake.h +629 -0
- decision_graph/decision_tree/bake/c_bake.pxd +52 -0
- decision_graph/decision_tree/bake/c_bake.pyi +151 -0
- decision_graph/decision_tree/bake/c_bake.pyx +142 -0
- decision_graph/decision_tree/bake/c_collections.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_collections.h +692 -0
- decision_graph/decision_tree/bake/c_collections.pxd +52 -0
- decision_graph/decision_tree/bake/c_collections.pyi +193 -0
- decision_graph/decision_tree/bake/c_collections.pyx +161 -0
- decision_graph/decision_tree/bake/c_const.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_const.h +395 -0
- decision_graph/decision_tree/bake/c_const.pxd +56 -0
- decision_graph/decision_tree/bake/c_const.pyi +305 -0
- decision_graph/decision_tree/bake/c_const.pyx +278 -0
- decision_graph/decision_tree/bake/c_edge.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_edge.h +511 -0
- decision_graph/decision_tree/bake/c_edge.pxd +117 -0
- decision_graph/decision_tree/bake/c_edge.pyi +192 -0
- decision_graph/decision_tree/bake/c_edge.pyx +270 -0
- decision_graph/decision_tree/bake/c_eval.h +848 -0
- decision_graph/decision_tree/bake/c_eval.pxd +40 -0
- decision_graph/decision_tree/bake/c_expr.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_expr.h +1900 -0
- decision_graph/decision_tree/bake/c_expr.pxd +129 -0
- decision_graph/decision_tree/bake/c_expr.pyi +280 -0
- decision_graph/decision_tree/bake/c_expr.pyx +272 -0
- decision_graph/decision_tree/bake/c_hierarchy.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_hierarchy.h +731 -0
- decision_graph/decision_tree/bake/c_hierarchy.pxd +71 -0
- decision_graph/decision_tree/bake/c_hierarchy.pyi +409 -0
- decision_graph/decision_tree/bake/c_hierarchy.pyx +277 -0
- decision_graph/decision_tree/bake/c_logic_group.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_logic_group.h +1226 -0
- decision_graph/decision_tree/bake/c_logic_group.pxd +162 -0
- decision_graph/decision_tree/bake/c_logic_group.pyi +264 -0
- decision_graph/decision_tree/bake/c_logic_group.pyx +308 -0
- decision_graph/decision_tree/bake/c_node.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_node.h +2660 -0
- decision_graph/decision_tree/bake/c_node.pxd +381 -0
- decision_graph/decision_tree/bake/c_node.pyi +438 -0
- decision_graph/decision_tree/bake/c_node.pyx +639 -0
- decision_graph/decision_tree/bake/c_reconstruct.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_reconstruct.pxd +63 -0
- decision_graph/decision_tree/bake/c_reconstruct.pyi +60 -0
- decision_graph/decision_tree/bake/c_reconstruct.pyx +252 -0
- decision_graph/decision_tree/bake/c_var.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/bake/c_var.h +2054 -0
- decision_graph/decision_tree/bake/c_var.pxd +205 -0
- decision_graph/decision_tree/bake/c_var.pyi +271 -0
- decision_graph/decision_tree/bake/c_var.pyx +228 -0
- decision_graph/decision_tree/capi/__infra__.pxd +33 -0
- decision_graph/decision_tree/capi/__init__.pxd +33 -0
- decision_graph/decision_tree/capi/__init__.py +57 -0
- decision_graph/decision_tree/capi/c_abc.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/capi/c_abc.pxd +246 -0
- decision_graph/decision_tree/capi/c_abc.pyi +770 -0
- decision_graph/decision_tree/capi/c_abc.pyx +1532 -0
- decision_graph/decision_tree/capi/c_collection.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/capi/c_collection.pxd +22 -0
- decision_graph/decision_tree/capi/c_collection.pyi +245 -0
- decision_graph/decision_tree/capi/c_collection.pyx +138 -0
- decision_graph/decision_tree/capi/c_node.cp315-win_amd64.pyd +0 -0
- decision_graph/decision_tree/capi/c_node.pxd +95 -0
- decision_graph/decision_tree/capi/c_node.pyi +492 -0
- decision_graph/decision_tree/capi/c_node.pyx +551 -0
- decision_graph/decision_tree/exc.py +189 -0
- decision_graph/decision_tree/native/__init__.py +57 -0
- decision_graph/decision_tree/native/abc.py +1167 -0
- decision_graph/decision_tree/native/collection.py +134 -0
- decision_graph/decision_tree/native/node.py +521 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_action.c +16320 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_action.h +324 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_allocator_protocol.c +12473 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_bake.c +16485 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_bake.h +629 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_collections.c +17381 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_collections.h +692 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_const.c +20815 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_const.h +395 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_edge.c +20696 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_edge.h +511 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_eval.h +848 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_expr.c +22065 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_expr.h +1900 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_hierarchy.c +23267 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_hierarchy.h +731 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_logic_group.c +22434 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_logic_group.h +1226 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_node.c +28385 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_node.h +2660 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_reconstruct.c +15955 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_var.c +16881 -0
- decision_graph/includes/decision_graph/decision_tree/bake/c_var.h +2054 -0
- decision_graph/includes/decision_graph/decision_tree/capi/c_abc.c +59196 -0
- decision_graph/includes/decision_graph/decision_tree/capi/c_collection.c +19967 -0
- decision_graph/includes/decision_graph/decision_tree/capi/c_node.c +39229 -0
- decision_graph/logic_group/__init__.py +25 -0
- decision_graph/logic_group/base.py +100 -0
- decision_graph/logic_group/pending_request.py +384 -0
- decision_graph/webui/__init__.py +24 -0
- decision_graph/webui/bake/__init__.py +65 -0
- decision_graph/webui/bake/app.py +488 -0
- decision_graph/webui/bake/static/d3.v7.min.js +2 -0
- decision_graph/webui/bake/static/script.js +1440 -0
- decision_graph/webui/bake/static/style.css +920 -0
- decision_graph/webui/bake/templates/index.html +183 -0
- decision_graph/webui/bake/templates/offline.html +184 -0
- decision_graph/webui/capi/__init__.py +54 -0
- decision_graph/webui/capi/app.py +366 -0
- decision_graph/webui/capi/main.py +53 -0
- decision_graph/webui/capi/static/d3.v7.min.js +2 -0
- decision_graph/webui/capi/static/script.js +1026 -0
- decision_graph/webui/capi/static/style.css +656 -0
- decision_graph/webui/capi/templates/index.html +93 -0
- decision_graph/webui/capi/templates/offline.html +94 -0
- decision_graph/webui/native/__init__.py +54 -0
- decision_graph/webui/native/app.py +366 -0
- decision_graph/webui/native/main.py +53 -0
- decision_graph/webui/native/static/d3.v7.min.js +2 -0
- decision_graph/webui/native/static/script.js +1026 -0
- decision_graph/webui/native/static/style.css +656 -0
- decision_graph/webui/native/templates/index.html +93 -0
- decision_graph/webui/native/templates/offline.html +94 -0
- pydecisiongraph-0.3.2.post1.dist-info/METADATA +136 -0
- pydecisiongraph-0.3.2.post1.dist-info/RECORD +148 -0
- pydecisiongraph-0.3.2.post1.dist-info/WHEEL +5 -0
- pydecisiongraph-0.3.2.post1.dist-info/licenses/LICENSE +373 -0
- pydecisiongraph-0.3.2.post1.dist-info/top_level.txt +2 -0
- tests/bake_c_evaluator.cp315-win_amd64.pyd +0 -0
- tests/bake_eval_probe.cp315-win_amd64.pyd +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from .. import LOGGER
|
|
4
|
+
|
|
5
|
+
LOGGER = LOGGER.getChild("LogicGroup")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def set_logger(logger: logging.Logger):
|
|
9
|
+
global LOGGER
|
|
10
|
+
LOGGER = logger
|
|
11
|
+
|
|
12
|
+
base.LOGGER = logger.getChild('base')
|
|
13
|
+
pending_request.LOGGER = logger.getChild('delayed')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from .base import *
|
|
17
|
+
from .pending_request import *
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
'SignalLogicGroup', 'InstantConfirmationLogicGroup',
|
|
21
|
+
|
|
22
|
+
'RequestType', 'RequestStatus', 'PendingRequest',
|
|
23
|
+
'RequestRegistered', 'RequestDenied', 'RequestConfirmed',
|
|
24
|
+
'DelayedConfirmationLogicGroup'
|
|
25
|
+
]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Literal, Any, overload, final
|
|
4
|
+
|
|
5
|
+
from . import LOGGER
|
|
6
|
+
from ..decision_tree import USING_CAPI, AttrExpression, LogicGroup, ActionNode, LGM, LongAction, ShortAction, NoAction
|
|
7
|
+
|
|
8
|
+
LOGGER = LOGGER.getChild('base')
|
|
9
|
+
|
|
10
|
+
__all__ = ['SignalLogicGroup', 'InstantConfirmationLogicGroup']
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SignalLogicGroup(LogicGroup):
|
|
14
|
+
def __new__(
|
|
15
|
+
cls,
|
|
16
|
+
*,
|
|
17
|
+
name: str = None,
|
|
18
|
+
parent: LogicGroup = None,
|
|
19
|
+
contexts: dict[str, Any] = None,
|
|
20
|
+
**kwargs
|
|
21
|
+
):
|
|
22
|
+
instance = super().__new__(
|
|
23
|
+
cls,
|
|
24
|
+
name=name,
|
|
25
|
+
parent=parent,
|
|
26
|
+
contexts=contexts
|
|
27
|
+
)
|
|
28
|
+
instance.signal = 0
|
|
29
|
+
return instance
|
|
30
|
+
|
|
31
|
+
def get(self, attr: str, dtype: type = None, repr: str = None):
|
|
32
|
+
return AttrExpression(attr=attr, logic_group=self, dtype=dtype, repr=repr)
|
|
33
|
+
|
|
34
|
+
def reset(self):
|
|
35
|
+
self.signal = 0
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def signal(self):
|
|
39
|
+
return self.contexts.setdefault('signal', 0)
|
|
40
|
+
|
|
41
|
+
@signal.setter
|
|
42
|
+
def signal(self, value: int):
|
|
43
|
+
self.contexts['signal'] = value
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@final
|
|
47
|
+
class InstantConfirmationLogicGroup(SignalLogicGroup):
|
|
48
|
+
def __new__(
|
|
49
|
+
cls,
|
|
50
|
+
*,
|
|
51
|
+
name: str = None,
|
|
52
|
+
parent: LogicGroup = None,
|
|
53
|
+
contexts: dict[str, Any] = None,
|
|
54
|
+
**kwargs
|
|
55
|
+
):
|
|
56
|
+
if not parent:
|
|
57
|
+
parent = LGM.active_group
|
|
58
|
+
|
|
59
|
+
if not isinstance(parent, SignalLogicGroup):
|
|
60
|
+
raise TypeError('InstantConfirmationLogicGroup requires a SignalLogicGroup as parent!')
|
|
61
|
+
|
|
62
|
+
instance = super().__new__(
|
|
63
|
+
cls,
|
|
64
|
+
name=f'{parent.name}.Instant' if not name else name,
|
|
65
|
+
parent=parent,
|
|
66
|
+
contexts=contexts
|
|
67
|
+
)
|
|
68
|
+
return instance
|
|
69
|
+
|
|
70
|
+
def reset(self):
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
@overload
|
|
74
|
+
def confirm(self, sig: Literal[1]) -> LongAction:
|
|
75
|
+
...
|
|
76
|
+
|
|
77
|
+
@overload
|
|
78
|
+
def confirm(self, sig: Literal[-1]) -> ShortAction:
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
def confirm(self, sig: Literal[-1, 1]) -> ActionNode:
|
|
82
|
+
self.signal = sig
|
|
83
|
+
|
|
84
|
+
if sig > 0:
|
|
85
|
+
return LongAction(sig=sig)
|
|
86
|
+
elif sig < 0:
|
|
87
|
+
return ShortAction(sig=sig)
|
|
88
|
+
|
|
89
|
+
if not LGM.inspection_mode:
|
|
90
|
+
LOGGER.warning(f'{self} received a confirmation of {sig=}! Which is not expected.')
|
|
91
|
+
|
|
92
|
+
return NoAction()
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def signal(self):
|
|
96
|
+
return self.parent.signal
|
|
97
|
+
|
|
98
|
+
@signal.setter
|
|
99
|
+
def signal(self, value: int):
|
|
100
|
+
self.parent.signal = value
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import enum
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
6
|
+
import uuid
|
|
7
|
+
from math import inf
|
|
8
|
+
from typing import Literal, Any
|
|
9
|
+
|
|
10
|
+
from algo_engine.exchange_profile import PROFILE
|
|
11
|
+
|
|
12
|
+
from . import LOGGER
|
|
13
|
+
from .base import SignalLogicGroup
|
|
14
|
+
from ..decision_tree import ActionNode, LogicGroup, NodeValueError, LongAction, ShortAction, LGM, NoAction
|
|
15
|
+
|
|
16
|
+
LOGGER = LOGGER.getChild('Request')
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
'RequestType', 'RequestStatus', 'PendingRequest',
|
|
20
|
+
'RequestRegistered', 'RequestDenied', 'RequestConfirmed',
|
|
21
|
+
'DelayedConfirmationLogicGroup'
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RequestType(enum.StrEnum):
|
|
26
|
+
generic = enum.auto()
|
|
27
|
+
open = enum.auto()
|
|
28
|
+
unwind = enum.auto()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RequestStatus(enum.StrEnum):
|
|
32
|
+
active = enum.auto()
|
|
33
|
+
idle = enum.auto()
|
|
34
|
+
locked = enum.auto()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class PendingRequest(object):
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
name: str = None,
|
|
41
|
+
uid: uuid.UUID = None,
|
|
42
|
+
**kwargs
|
|
43
|
+
):
|
|
44
|
+
self.uid = uid or uuid.uuid4()
|
|
45
|
+
self.name = name or f'PendingRequest.{self.uid}'
|
|
46
|
+
self.logic_group = kwargs.get('logic_group', LGM.active_group)
|
|
47
|
+
|
|
48
|
+
self.state = {
|
|
49
|
+
'sig': kwargs.get('sig', 0),
|
|
50
|
+
'rtype': kwargs.get('rtype', RequestType.generic),
|
|
51
|
+
'rstatus': kwargs.get('rstatus', RequestStatus.idle),
|
|
52
|
+
'activated_ts': kwargs.get('activated_ts', 0),
|
|
53
|
+
'activated_by': kwargs.get('activated_by'),
|
|
54
|
+
'deactivated_by': kwargs.get('deactivated_by'),
|
|
55
|
+
'confirmed_by': kwargs.get('confirmed_by'),
|
|
56
|
+
'timeout': kwargs.get('timeout', inf),
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
self.register_node = {}
|
|
60
|
+
self.confirmation_node = {}
|
|
61
|
+
self.denial_node = {}
|
|
62
|
+
self.lock = threading.Lock()
|
|
63
|
+
|
|
64
|
+
def bind(self, node_id: uuid.UUID, logic_node: RequestRegistered | RequestDenied | RequestConfirmed):
|
|
65
|
+
if isinstance(logic_node, RequestRegistered):
|
|
66
|
+
self.register_node[node_id] = logic_node
|
|
67
|
+
elif isinstance(logic_node, RequestDenied):
|
|
68
|
+
self.denial_node[node_id] = logic_node
|
|
69
|
+
elif isinstance(logic_node, RequestConfirmed):
|
|
70
|
+
self.confirmation_node[node_id] = logic_node
|
|
71
|
+
else:
|
|
72
|
+
raise TypeError('PendingRequest can only be bound to RequestRegistered, RequestConfirmed, or RequestDenied nodes!')
|
|
73
|
+
return logic_node
|
|
74
|
+
|
|
75
|
+
def activate(self, node_id: uuid.UUID):
|
|
76
|
+
if self.rstatus == RequestStatus.active:
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
if node_id not in self.register_node:
|
|
80
|
+
raise NodeValueError('The provided node_id is not bound with this PendingRequest!')
|
|
81
|
+
|
|
82
|
+
reg_node = self.register_node[node_id]
|
|
83
|
+
|
|
84
|
+
if not reg_node.sig:
|
|
85
|
+
raise NodeValueError('Signal Must not be zero.')
|
|
86
|
+
|
|
87
|
+
if not self.lock.acquire(blocking=False):
|
|
88
|
+
raise NodeValueError('Cannot activate a locked PendingRequest!')
|
|
89
|
+
|
|
90
|
+
self.state['sig'] = reg_node.sig
|
|
91
|
+
self.state['rtype'] = reg_node.rtype
|
|
92
|
+
self.state['rstatus'] = RequestStatus.active
|
|
93
|
+
self.state['activated_ts'] = self.logic_group.contexts.get('timestamp', time.time())
|
|
94
|
+
self.state['activated_by'] = reg_node
|
|
95
|
+
self.state['deactivated_by'] = None
|
|
96
|
+
self.state['confirmed_by'] = None
|
|
97
|
+
self.state['timeout'] = reg_node.timeout
|
|
98
|
+
|
|
99
|
+
self.lock.release()
|
|
100
|
+
|
|
101
|
+
def deactivate(self, node_id: uuid.UUID):
|
|
102
|
+
if self.rstatus == RequestStatus.idle:
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
if node_id not in self.denial_node:
|
|
106
|
+
raise NodeValueError('The provided node_id is not bound with this PendingRequest!')
|
|
107
|
+
|
|
108
|
+
node = self.denial_node[node_id]
|
|
109
|
+
|
|
110
|
+
if not self.lock.acquire(blocking=False):
|
|
111
|
+
raise NodeValueError('Cannot deactivate a locked PendingRequest!')
|
|
112
|
+
|
|
113
|
+
self.state['sig'] = 0
|
|
114
|
+
self.state['rtype'] = RequestType.generic
|
|
115
|
+
self.state['rstatus'] = RequestStatus.idle
|
|
116
|
+
self.state['activated_ts'] = 0
|
|
117
|
+
self.state['activated_by'] = None
|
|
118
|
+
self.state['deactivated_by'] = node
|
|
119
|
+
self.state['confirmed_by'] = None
|
|
120
|
+
self.state['timeout'] = 0
|
|
121
|
+
|
|
122
|
+
self.lock.release()
|
|
123
|
+
|
|
124
|
+
def confirm(self, node_id: uuid.UUID) -> ShortAction | NoAction | LongAction:
|
|
125
|
+
if self.rstatus != RequestStatus.active:
|
|
126
|
+
raise NodeValueError('Cannot confirm a inactive PendingRequest!')
|
|
127
|
+
|
|
128
|
+
if node_id not in self.confirmation_node:
|
|
129
|
+
raise NodeValueError('The provided node_id is not bound with this PendingRequest!')
|
|
130
|
+
|
|
131
|
+
node = self.confirmation_node[node_id]
|
|
132
|
+
|
|
133
|
+
if not self.lock.acquire(blocking=False):
|
|
134
|
+
raise NodeValueError('Cannot confirm a locked PendingRequest!')
|
|
135
|
+
|
|
136
|
+
# Step 1: Lock the request
|
|
137
|
+
self.state['rstatus'] = RequestStatus.locked
|
|
138
|
+
prelaunch_check = True
|
|
139
|
+
|
|
140
|
+
# Step 2: Check timeout
|
|
141
|
+
current_ts = self.logic_group.contexts.get('timestamp', time.time())
|
|
142
|
+
activated_ts = self.state['activated_ts']
|
|
143
|
+
timeout = self.state['timeout']
|
|
144
|
+
elapsed_time = PROFILE.trading_time_between(activated_ts, current_ts)
|
|
145
|
+
if elapsed_time >= timeout:
|
|
146
|
+
prelaunch_check = False
|
|
147
|
+
|
|
148
|
+
# Step 3: Check signal match
|
|
149
|
+
if prelaunch_check and node.sig != self.sig:
|
|
150
|
+
prelaunch_check = False
|
|
151
|
+
|
|
152
|
+
# Step 4: Finalize confirmation or denial
|
|
153
|
+
if prelaunch_check:
|
|
154
|
+
if node.sig > 0:
|
|
155
|
+
action = LongAction(sig=node.sig, auto_connect=False)
|
|
156
|
+
elif node.sig < 0:
|
|
157
|
+
action = ShortAction(sig=node.sig, auto_connect=False)
|
|
158
|
+
else:
|
|
159
|
+
action = NoAction(auto_connect=False)
|
|
160
|
+
else:
|
|
161
|
+
action = NoAction(auto_connect=False)
|
|
162
|
+
|
|
163
|
+
# Step 5: Reset the request state
|
|
164
|
+
self.state['sig'] = 0
|
|
165
|
+
self.state['rtype'] = RequestType.generic
|
|
166
|
+
self.state['rstatus'] = RequestStatus.idle
|
|
167
|
+
self.state['activated_ts'] = 0
|
|
168
|
+
self.state['activated_by'] = None
|
|
169
|
+
self.state['deactivated_by'] = None
|
|
170
|
+
self.state['confirmed_by'] = node
|
|
171
|
+
self.state['timeout'] = 0
|
|
172
|
+
|
|
173
|
+
# Step 6: Release the lock and return the action
|
|
174
|
+
self.lock.release()
|
|
175
|
+
return action
|
|
176
|
+
|
|
177
|
+
def __bool__(self):
|
|
178
|
+
if not self.sig:
|
|
179
|
+
return False
|
|
180
|
+
return True
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def sig(self) -> int:
|
|
184
|
+
return self.state['sig']
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def rtype(self) -> RequestType:
|
|
188
|
+
return self.state['rtype']
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def rstatus(self) -> RequestStatus:
|
|
192
|
+
return self.state['rstatus']
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def activated_ts(self) -> float:
|
|
196
|
+
return self.state['activated_ts']
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def activated_by(self) -> RequestRegistered:
|
|
200
|
+
return self.state['activated_by']
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def deactivated_by(self) -> RequestDenied:
|
|
204
|
+
return self.state['deactivated_by']
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def confirmed_by(self) -> RequestConfirmed:
|
|
208
|
+
return self.state['confirmed_by']
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def timeout(self) -> float:
|
|
212
|
+
return self.state['timeout']
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class RequestRegistered(ActionNode):
|
|
216
|
+
def __new__(
|
|
217
|
+
cls,
|
|
218
|
+
*,
|
|
219
|
+
sig: Literal[-1, 1],
|
|
220
|
+
req: PendingRequest,
|
|
221
|
+
rtype: RequestType = RequestType.generic,
|
|
222
|
+
timeout: float = float('inf'),
|
|
223
|
+
action=None,
|
|
224
|
+
expression=None,
|
|
225
|
+
dtype=None,
|
|
226
|
+
repr=None,
|
|
227
|
+
auto_connect: bool = True,
|
|
228
|
+
**kwargs
|
|
229
|
+
):
|
|
230
|
+
uid = uuid.uuid4()
|
|
231
|
+
|
|
232
|
+
if action is None:
|
|
233
|
+
action = lambda: req.activate(uid)
|
|
234
|
+
|
|
235
|
+
if expression is None:
|
|
236
|
+
expression = NoAction(auto_connect=False)
|
|
237
|
+
|
|
238
|
+
instance = super().__new__(
|
|
239
|
+
cls,
|
|
240
|
+
action=action,
|
|
241
|
+
expression=expression,
|
|
242
|
+
dtype=dtype,
|
|
243
|
+
repr=repr or f'<Pending Request Registered {sig=}>',
|
|
244
|
+
auto_connect=auto_connect
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
instance.uid = uid
|
|
248
|
+
instance.sig = sig
|
|
249
|
+
instance.req = req
|
|
250
|
+
instance.rtype = rtype
|
|
251
|
+
instance.timeout = timeout
|
|
252
|
+
|
|
253
|
+
return req.bind(uid, instance)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class RequestDenied(ActionNode):
|
|
257
|
+
def __new__(
|
|
258
|
+
cls,
|
|
259
|
+
*,
|
|
260
|
+
req: PendingRequest,
|
|
261
|
+
action=None,
|
|
262
|
+
expression=None,
|
|
263
|
+
dtype=None,
|
|
264
|
+
repr=None,
|
|
265
|
+
auto_connect: bool = True,
|
|
266
|
+
**kwargs
|
|
267
|
+
):
|
|
268
|
+
uid = uuid.uuid4()
|
|
269
|
+
|
|
270
|
+
if action is None:
|
|
271
|
+
action = lambda: req.deactivate(uid)
|
|
272
|
+
|
|
273
|
+
if expression is None:
|
|
274
|
+
expression = NoAction(auto_connect=False)
|
|
275
|
+
|
|
276
|
+
instance = super().__new__(
|
|
277
|
+
cls,
|
|
278
|
+
action=action,
|
|
279
|
+
expression=expression,
|
|
280
|
+
dtype=dtype,
|
|
281
|
+
repr=repr or '<Pending Request Denied>',
|
|
282
|
+
auto_connect=auto_connect
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
instance.uid = uid
|
|
286
|
+
instance.req = req
|
|
287
|
+
return req.bind(uid, instance)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class RequestConfirmed(ActionNode):
|
|
291
|
+
def __new__(
|
|
292
|
+
cls,
|
|
293
|
+
*,
|
|
294
|
+
sig: Literal[-1, 1],
|
|
295
|
+
req: PendingRequest,
|
|
296
|
+
action=None,
|
|
297
|
+
expression=None,
|
|
298
|
+
dtype=None,
|
|
299
|
+
repr=None,
|
|
300
|
+
auto_connect: bool = True,
|
|
301
|
+
**kwargs
|
|
302
|
+
):
|
|
303
|
+
uid = uuid.uuid4()
|
|
304
|
+
|
|
305
|
+
if action is None:
|
|
306
|
+
action = lambda: req.confirm(uid)
|
|
307
|
+
|
|
308
|
+
if expression is None:
|
|
309
|
+
expression = lambda: req.confirm(uid)
|
|
310
|
+
|
|
311
|
+
instance = super().__new__(
|
|
312
|
+
cls,
|
|
313
|
+
action=action,
|
|
314
|
+
expression=expression,
|
|
315
|
+
dtype=dtype,
|
|
316
|
+
repr=repr or f'<Pending Request Confirmed {sig=}>',
|
|
317
|
+
auto_connect=auto_connect
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
instance.uid = uid
|
|
321
|
+
instance.sig = sig
|
|
322
|
+
instance.req = req
|
|
323
|
+
return req.bind(uid, instance)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class DelayedConfirmationLogicGroup(SignalLogicGroup):
|
|
327
|
+
def __new__(
|
|
328
|
+
cls,
|
|
329
|
+
*,
|
|
330
|
+
name: str = None,
|
|
331
|
+
parent: LogicGroup = None,
|
|
332
|
+
contexts: dict[str, Any] = None,
|
|
333
|
+
**kwargs
|
|
334
|
+
):
|
|
335
|
+
if not parent:
|
|
336
|
+
parent = LGM.active_group
|
|
337
|
+
|
|
338
|
+
if not isinstance(parent, SignalLogicGroup):
|
|
339
|
+
raise TypeError('InstantConfirmationLogicGroup requires a SignalLogicGroup as parent!')
|
|
340
|
+
|
|
341
|
+
instance = super().__new__(
|
|
342
|
+
cls,
|
|
343
|
+
name=f'{parent.name}.Instant' if not name else name,
|
|
344
|
+
parent=parent,
|
|
345
|
+
contexts=contexts
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
instance.req = instance.contexts['pending_request'] = PendingRequest()
|
|
349
|
+
return instance
|
|
350
|
+
|
|
351
|
+
def register(self, sig: Literal[1, -1], timeout: float = float('inf'), rtype: RequestType = RequestType.generic) -> RequestRegistered:
|
|
352
|
+
action_register = RequestRegistered(
|
|
353
|
+
sig=sig,
|
|
354
|
+
req=self.req,
|
|
355
|
+
rtype=rtype,
|
|
356
|
+
timeout=timeout
|
|
357
|
+
)
|
|
358
|
+
return action_register
|
|
359
|
+
|
|
360
|
+
def deny(self) -> RequestDenied:
|
|
361
|
+
action_deny = RequestDenied(
|
|
362
|
+
req=self.req
|
|
363
|
+
)
|
|
364
|
+
return action_deny
|
|
365
|
+
|
|
366
|
+
def confirm(self, sig: Literal[1, -1]) -> RequestConfirmed:
|
|
367
|
+
action_confirm = RequestConfirmed(
|
|
368
|
+
sig=sig,
|
|
369
|
+
req=self.req
|
|
370
|
+
)
|
|
371
|
+
return action_confirm
|
|
372
|
+
|
|
373
|
+
def reset(self):
|
|
374
|
+
self.req.reset()
|
|
375
|
+
super().reset()
|
|
376
|
+
|
|
377
|
+
@property
|
|
378
|
+
def signal(self):
|
|
379
|
+
return self.parent.signal
|
|
380
|
+
|
|
381
|
+
@signal.setter
|
|
382
|
+
def signal(self, value: Literal[-1, 0, 1]):
|
|
383
|
+
assert isinstance(value, (int, float))
|
|
384
|
+
self.parent.signal = value
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""The web UIs — one per API surface.
|
|
2
|
+
|
|
3
|
+
Each layer gets its own sub-package and reads that layer directly:
|
|
4
|
+
|
|
5
|
+
- ``.capi`` ``decision_graph.decision_tree.capi`` — the Cython C API
|
|
6
|
+
- ``.native`` ``decision_graph.decision_tree.native`` — the pure-Python model
|
|
7
|
+
- ``.bake`` ``decision_graph.decision_tree.bake`` — the bake layer
|
|
8
|
+
|
|
9
|
+
Nothing is imported here on purpose: a UI drags in Flask and a directory of
|
|
10
|
+
browser assets, and importing this package is not asking for either. Reach for
|
|
11
|
+
the layer you want, by name::
|
|
12
|
+
|
|
13
|
+
from decision_graph.webui.bake import to_html
|
|
14
|
+
|
|
15
|
+
The three share no code, also on purpose. A visualiser is worth reading only if
|
|
16
|
+
it shows what ITS layer actually built, and a helper shared between two layers
|
|
17
|
+
is the first place a real difference between them gets papered over.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
|
|
22
|
+
# The layer's logger hangs off the root one, so a consumer that re-points
|
|
23
|
+
# `decision_graph.LOGGER` re-points every UI with it.
|
|
24
|
+
LOGGER = logging.getLogger("DecisionGraph").getChild("WebUI")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""The web UI of the bake layer (``decision_graph.decision_tree.bake``).
|
|
2
|
+
|
|
3
|
+
The graph it draws is a bake graph — the same node set the bake protocol
|
|
4
|
+
prepares for a hot walk — read through the bake surface alone; see ``.app`` for
|
|
5
|
+
the one place that surface is named.
|
|
6
|
+
|
|
7
|
+
A bake graph comes in two states and the page draws both:
|
|
8
|
+
|
|
9
|
+
- UNBAKED, the way a build leaves it: the breakpoints a split build stopped at
|
|
10
|
+
are still in the graph, and a branch that was reserved and never filled is
|
|
11
|
+
still a placeholder;
|
|
12
|
+
- BAKED, the way a walk sees it: the breakpoints are gone (what they carried
|
|
13
|
+
took their place and their arm), the stand-ins are consolidated, and the
|
|
14
|
+
graph is sealed and locked.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
|
|
19
|
+
from .. import LOGGER
|
|
20
|
+
from decision_graph.decision_tree.bake.c_hierarchy import RootLogicNode
|
|
21
|
+
from decision_graph.decision_tree.bake.c_node import LogicNode
|
|
22
|
+
|
|
23
|
+
LOGGER = LOGGER.getChild('WebUI.Bake')
|
|
24
|
+
|
|
25
|
+
from . import app
|
|
26
|
+
from .app import BakeWebUi
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def show(root: LogicNode, with_eval: bool = True, **kwargs):
|
|
30
|
+
_app = BakeWebUi(
|
|
31
|
+
host=kwargs.get('host', "127.0.0.1"),
|
|
32
|
+
port=kwargs.get('port', 5000),
|
|
33
|
+
debug=kwargs.get('debug', False)
|
|
34
|
+
)
|
|
35
|
+
_app.show(
|
|
36
|
+
node=root,
|
|
37
|
+
with_eval=with_eval
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def watch(root: RootLogicNode, interval: float = .5, block: bool = False, **kwargs):
|
|
42
|
+
_app = BakeWebUi(
|
|
43
|
+
host=kwargs.get('host', "127.0.0.1"),
|
|
44
|
+
port=kwargs.get('port', 5000),
|
|
45
|
+
debug=kwargs.get('debug', False)
|
|
46
|
+
)
|
|
47
|
+
_app.watch(
|
|
48
|
+
node=root,
|
|
49
|
+
interval=interval,
|
|
50
|
+
block=block
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def to_html(root: LogicNode, file_name: str, with_eval: bool = True):
|
|
55
|
+
BakeWebUi.to_html(
|
|
56
|
+
node=root,
|
|
57
|
+
file_name=file_name,
|
|
58
|
+
with_eval=with_eval
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def set_logger(logger: logging.Logger):
|
|
63
|
+
global LOGGER
|
|
64
|
+
LOGGER = logger.getChild('WebUI.Bake')
|
|
65
|
+
app.LOGGER = LOGGER
|