PyAlgoEngine 0.7.7a1__tar.gz → 0.7.7a2__tar.gz
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.
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/PKG-INFO +1 -1
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/PyAlgoEngine.egg-info/PKG-INFO +1 -1
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/__init__.py +1 -1
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/sim_input/client.py +133 -41
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/LICENSE +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/PyAlgoEngine.egg-info/SOURCES.txt +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/PyAlgoEngine.egg-info/dependency_links.txt +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/PyAlgoEngine.egg-info/requires.txt +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/PyAlgoEngine.egg-info/top_level.txt +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/README.md +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/__init__.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/backtest/__init__.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/backtest/doc_server.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/backtest/tester.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/backtest/web_app.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/bokeh_server.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/demo/__init__.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/demo/test.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/sim_input/__init__.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/sim_input/sim_keyboard.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/sim_input/sim_mouse.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/apps/sim_input/window.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/backtest/__init__.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/backtest/__main__.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/backtest/metrics.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/backtest/replay.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/backtest/sim_match.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/base/__init__.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/base/console_utils.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/base/finance_decimal.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/base/market_buffer.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/base/market_utils.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/base/market_utils_nt.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/base/market_utils_posix.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/base/technical_analysis.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/base/telemetrics.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/base/trade_utils.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/engine/__init__.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/engine/algo_engine.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/engine/event_engine.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/engine/market_engine.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/engine/trade_engine.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/monitor/__init__.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/monitor/advanced_data_interface.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/profile/__init__.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/profile/cn.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/strategy/__init__.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/strategy/strategy_engine.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/utils/__init__.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/utils/commit_regularizer.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/utils/data_utils.py +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/setup.cfg +0 -0
- {pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/setup.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
__package__ = 'algo_engine.apps.sim_input'
|
|
2
2
|
|
|
3
3
|
import abc
|
|
4
|
-
import
|
|
4
|
+
from threading import Thread
|
|
5
5
|
import time
|
|
6
6
|
import tkinter
|
|
7
7
|
from collections.abc import Callable, Iterable
|
|
@@ -63,53 +63,143 @@ class Action(object):
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
class AutoWorkClient(object, metaclass=abc.ABCMeta):
|
|
66
|
-
def __init__(self, root=None):
|
|
66
|
+
def __init__(self, root=None, **kwargs):
|
|
67
67
|
self.root = root if root is not None else tkinter.Tk()
|
|
68
|
-
self.root.title("Structured GUI Client")
|
|
69
|
-
|
|
68
|
+
self.root.title(kwargs.get('title', "Structured GUI Client"))
|
|
69
|
+
|
|
70
|
+
self.actions: dict[int, list[Action]] = {}
|
|
71
|
+
self.layout = {}
|
|
70
72
|
|
|
71
73
|
# State variables
|
|
72
74
|
self.worker_thread = None
|
|
73
75
|
self.running = False
|
|
74
76
|
|
|
75
|
-
|
|
76
|
-
self.button = ttk.Button(root, text="Takeover Input", command=self.toggle_auto_work)
|
|
77
|
-
self.button.pack(pady=10)
|
|
78
|
-
|
|
79
|
-
# Create table
|
|
80
|
-
self.table = ttk.Treeview(
|
|
81
|
-
root,
|
|
82
|
-
columns=("Action", "Status", "Comments"),
|
|
83
|
-
show="tree headings",
|
|
84
|
-
height=20
|
|
85
|
-
)
|
|
86
|
-
self.table.heading("Action", text="Action")
|
|
87
|
-
self.table.heading("Status", text="Status")
|
|
88
|
-
self.table.heading("Comments", text="Comments")
|
|
89
|
-
self.table.pack(padx=10, pady=10, fill="both", expand=True)
|
|
77
|
+
self.render_layout()
|
|
90
78
|
|
|
91
|
-
|
|
79
|
+
def render_layout(self):
|
|
80
|
+
# No fixed geometry, letting it resize
|
|
81
|
+
# self.root.geometry("600x500")
|
|
82
|
+
|
|
83
|
+
# Grid(0, 0): Create button
|
|
84
|
+
button_takeover = self.layout['button_takeover'] = ttk.Button(self.root, text="Takeover Input", command=self.toggle_auto_work)
|
|
85
|
+
button_takeover.grid(row=0, column=0, columnspan=2, pady=10, sticky="ew")
|
|
86
|
+
|
|
87
|
+
# Grid(1, 0): Mock button
|
|
88
|
+
button_mock = self.layout['button_mock'] = ttk.Button(self.root, text="Mock Action", command=self.toggle_mock)
|
|
89
|
+
button_mock.grid(row=1, column=0, padx=10, pady=10, sticky="ew")
|
|
90
|
+
|
|
91
|
+
# Grid(1, 1): Dropdown for action selection
|
|
92
|
+
action_selected = self.layout['action_selected'] = tkinter.StringVar()
|
|
93
|
+
action_dropdown = self.layout['action_dropdown'] = ttk.Combobox(self.root, textvariable=action_selected, state="readonly")
|
|
94
|
+
action_dropdown.grid(row=1, column=1, padx=10, pady=10, sticky="ew")
|
|
95
|
+
|
|
96
|
+
# Grid(2, 0): Create table
|
|
97
|
+
action_table = self.layout['action_table'] = ttk.Treeview(self.root, columns=("Action", "Status", "Comments"), show="tree headings")
|
|
98
|
+
action_table.heading("Action", text="Action")
|
|
99
|
+
action_table.heading("Status", text="Status")
|
|
100
|
+
action_table.heading("Comments", text="Comments")
|
|
101
|
+
action_table.grid(row=2, column=0, columnspan=2, padx=10, pady=10, sticky="nsew")
|
|
102
|
+
|
|
103
|
+
# Make the window resize properly by configuring grid weights
|
|
104
|
+
self.root.grid_columnconfigure(0, weight=1, minsize=200) # Adjust minsize for column 0
|
|
105
|
+
self.root.grid_columnconfigure(1, weight=1, minsize=200) # Adjust minsize for column 1
|
|
106
|
+
self.root.grid_rowconfigure(2, weight=1) # Make row 2 expand with the window
|
|
92
107
|
|
|
93
108
|
@abc.abstractmethod
|
|
94
109
|
def listen_signal(self) -> int:
|
|
95
110
|
...
|
|
96
111
|
|
|
97
112
|
def register_action(self, action: Action, signal: int):
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
self.actions.setdefault(signal, []).append(action)
|
|
114
|
+
|
|
115
|
+
# Update the dropdown with all registered actions.
|
|
116
|
+
all_actions = [f"Signal {signal} - {action.name}" for signal, actions in self.actions.items() for action in actions]
|
|
117
|
+
action_dropdown = self.layout['action_dropdown']
|
|
118
|
+
action_dropdown["values"] = all_actions
|
|
119
|
+
|
|
120
|
+
def mock_action(self, action: Action, timeout: float = 3.):
|
|
121
|
+
"""Mock the execution of an action on a transparent test ground."""
|
|
122
|
+
self.root.iconify()
|
|
123
|
+
|
|
124
|
+
# Create a borderless, transparent test ground window
|
|
125
|
+
testground = tkinter.Toplevel(self.root)
|
|
126
|
+
# testground.overrideredirect(True) # Remove all window borders and decorations
|
|
127
|
+
|
|
128
|
+
# Get screen dimensions and set the testground window to cover the entire screen
|
|
129
|
+
# screen_width = self.root.winfo_screenwidth()
|
|
130
|
+
# screen_height = self.root.winfo_screenheight()
|
|
131
|
+
# testground.geometry(f"{screen_width}x{screen_height}+0+0")
|
|
132
|
+
|
|
133
|
+
# Make the window semi-transparent and ensure it stays on top
|
|
134
|
+
testground.attributes("-alpha", 0.5) # Set transparency to 50%
|
|
135
|
+
testground.attributes("-fullscreen", True)
|
|
136
|
+
testground.attributes("-topmost", True) # Ensure it stays above other windows
|
|
137
|
+
|
|
138
|
+
# Force the window manager to render the window completely
|
|
139
|
+
testground.lift() # Bring it to the front
|
|
140
|
+
testground.focus_force() # Force focus to this window
|
|
141
|
+
|
|
142
|
+
# Add a label to display input history
|
|
143
|
+
input_history = tkinter.Listbox(testground, font=("Courier", 14))
|
|
144
|
+
input_history.pack(fill="both", expand=True, padx=20, pady=20)
|
|
145
|
+
input_history.insert("active", f"Mocking {action.name}")
|
|
146
|
+
testground.update_idletasks()
|
|
147
|
+
|
|
148
|
+
# Simulate action execution
|
|
149
|
+
for step in action.steps:
|
|
150
|
+
# Display the step name in the input history
|
|
151
|
+
input_history.insert("end", f"Executing Step: {step['name']}")
|
|
152
|
+
input_history.insert("end", f"Comments: {step.get('comments', 'No comments')}")
|
|
153
|
+
input_history.insert("end", "-" * 50)
|
|
154
|
+
input_history.see("end")
|
|
155
|
+
testground.update_idletasks()
|
|
156
|
+
|
|
157
|
+
# Simulate mouse and keyboard inputs
|
|
158
|
+
procedure = step['procedure']
|
|
159
|
+
args = step.get('args', [])
|
|
160
|
+
kwargs = step.get('kwargs', {})
|
|
161
|
+
try:
|
|
162
|
+
procedure(*args, **kwargs)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
input_history.insert("end", f"Error: {str(e)}")
|
|
165
|
+
input_history.see("end")
|
|
166
|
+
|
|
167
|
+
# Simulate delay between steps
|
|
168
|
+
|
|
169
|
+
# Wait 3 seconds, then close the test ground and restore the client window
|
|
170
|
+
time.sleep(timeout)
|
|
171
|
+
testground.destroy()
|
|
102
172
|
|
|
103
173
|
def toggle_auto_work(self):
|
|
104
174
|
"""Toggle the daemon thread to start or stop auto work."""
|
|
105
175
|
if not self.running:
|
|
106
176
|
self.running = True
|
|
107
|
-
self.
|
|
108
|
-
self.worker_thread =
|
|
177
|
+
self.layout['button_takeover'].config(text="Release Control")
|
|
178
|
+
self.worker_thread = Thread(target=self.auto_work, daemon=True)
|
|
109
179
|
self.worker_thread.start()
|
|
110
180
|
else:
|
|
111
181
|
self.running = False
|
|
112
|
-
self.
|
|
182
|
+
self.layout['button_takeover'].config(text="Takeover Input")
|
|
183
|
+
|
|
184
|
+
def toggle_mock(self):
|
|
185
|
+
"""Mock the action selected in the dropdown."""
|
|
186
|
+
selected_name = self.layout['action_selected'].get()
|
|
187
|
+
if not selected_name:
|
|
188
|
+
LOGGER.warning("No action selected to mock!")
|
|
189
|
+
return
|
|
190
|
+
|
|
191
|
+
selected_action = None
|
|
192
|
+
for signal, actions in self.actions.items():
|
|
193
|
+
for action in actions:
|
|
194
|
+
if f"Signal {signal} - {action.name}" == selected_name:
|
|
195
|
+
selected_action = action
|
|
196
|
+
break
|
|
197
|
+
|
|
198
|
+
if selected_action is None:
|
|
199
|
+
LOGGER.warning(f"Action '{selected_name}' not found!")
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
self.mock_action(action=selected_action)
|
|
113
203
|
|
|
114
204
|
def auto_work(self):
|
|
115
205
|
"""Generate data for the table in a loop."""
|
|
@@ -150,19 +240,20 @@ class AutoWorkClient(object, metaclass=abc.ABCMeta):
|
|
|
150
240
|
|
|
151
241
|
def update_table(self, actions: list[Action]):
|
|
152
242
|
"""Render the table based on the provided actions."""
|
|
153
|
-
self.
|
|
243
|
+
action_table = self.layout['action_table']
|
|
244
|
+
action_table.delete(*action_table.get_children()) # Clear existing rows
|
|
154
245
|
|
|
155
246
|
for action_idx, action in enumerate(actions):
|
|
156
247
|
prefix = chr(0x250C)
|
|
157
248
|
# Insert the parent row
|
|
158
|
-
parent_id =
|
|
249
|
+
parent_id = action_table.insert(
|
|
159
250
|
"", "end", iid=action_idx, values=(f"{prefix} {action.name}", "Pending", "")
|
|
160
251
|
)
|
|
161
252
|
|
|
162
253
|
# Insert child rows for steps
|
|
163
254
|
for step_idx, step in enumerate(action.steps):
|
|
164
255
|
prefix = f'{chr(0x251C) if step_idx < len(action.steps) - 1 else chr(0x2514)}{chr(0x2500)}'
|
|
165
|
-
|
|
256
|
+
action_table.insert(
|
|
166
257
|
parent_id,
|
|
167
258
|
"end",
|
|
168
259
|
iid=f"{action_idx}-{step_idx}",
|
|
@@ -171,7 +262,7 @@ class AutoWorkClient(object, metaclass=abc.ABCMeta):
|
|
|
171
262
|
)
|
|
172
263
|
|
|
173
264
|
# Expand parent row by default
|
|
174
|
-
|
|
265
|
+
action_table.item(parent_id, open=True)
|
|
175
266
|
|
|
176
267
|
# Auto-adjust column widths
|
|
177
268
|
self.adjust_column_widths()
|
|
@@ -179,26 +270,27 @@ class AutoWorkClient(object, metaclass=abc.ABCMeta):
|
|
|
179
270
|
def update_status(self, parent_id: int, child_id: int = None, status: str = "Pending"):
|
|
180
271
|
"""Update the status of the specified row."""
|
|
181
272
|
row_id = f"{parent_id}" if child_id is None else f"{parent_id}-{child_id}"
|
|
182
|
-
|
|
273
|
+
action_table = self.layout['action_table']
|
|
274
|
+
values = action_table.item(row_id, "values")
|
|
183
275
|
|
|
184
276
|
# Select the row if the status is "Executing"
|
|
185
277
|
match status:
|
|
186
278
|
case "Executing":
|
|
187
|
-
|
|
188
|
-
|
|
279
|
+
action_table.selection_set(row_id)
|
|
280
|
+
action_table.see(row_id) # Ensure the row is visible
|
|
189
281
|
|
|
190
|
-
|
|
282
|
+
action_table.item(row_id, values=(values[0], status, values[2]))
|
|
191
283
|
|
|
192
284
|
def adjust_column_widths(self):
|
|
193
285
|
"""Automatically adjust column widths based on content."""
|
|
286
|
+
action_table = self.layout['action_table']
|
|
287
|
+
action_table.column("#0", width=30, minwidth=30, stretch=False)
|
|
194
288
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
for col in self.table["columns"]:
|
|
289
|
+
for col in action_table["columns"]:
|
|
198
290
|
max_width = max(
|
|
199
|
-
[len(str(
|
|
291
|
+
[len(str(action_table.set(item, col))) for item in action_table.get_children()] + [len(col)]
|
|
200
292
|
)
|
|
201
|
-
|
|
293
|
+
action_table.column(col, width=max_width * 10, minwidth=30, stretch=True) # Scale width for readability
|
|
202
294
|
|
|
203
295
|
|
|
204
296
|
class ExampleClient(AutoWorkClient):
|
|
@@ -249,5 +341,5 @@ def main():
|
|
|
249
341
|
|
|
250
342
|
|
|
251
343
|
if __name__ == "__main__":
|
|
252
|
-
t =
|
|
344
|
+
t = Thread(target=main, daemon=False)
|
|
253
345
|
t.start()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyalgoengine-0.7.7a1 → pyalgoengine-0.7.7a2}/algo_engine/monitor/advanced_data_interface.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|