PyAlgoEngine 0.7.5.post1__tar.gz → 0.7.6__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.5.post1 → pyalgoengine-0.7.6}/PKG-INFO +1 -1
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/PyAlgoEngine.egg-info/PKG-INFO +1 -1
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/PyAlgoEngine.egg-info/SOURCES.txt +4 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/__init__.py +1 -1
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/apps/bokeh_server.py +1 -0
- pyalgoengine-0.7.6/algo_engine/apps/sim_input/__init__.py +26 -0
- pyalgoengine-0.7.6/algo_engine/apps/sim_input/sim_keyboard.py +87 -0
- pyalgoengine-0.7.6/algo_engine/apps/sim_input/sim_mouse.py +136 -0
- pyalgoengine-0.7.6/algo_engine/apps/sim_input/window.py +162 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/base/market_utils_posix.py +2 -2
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/LICENSE +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/PyAlgoEngine.egg-info/dependency_links.txt +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/PyAlgoEngine.egg-info/requires.txt +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/PyAlgoEngine.egg-info/top_level.txt +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/README.md +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/apps/__init__.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/apps/backtest/__init__.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/apps/backtest/doc_server.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/apps/backtest/tester.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/apps/backtest/web_app.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/apps/demo/__init__.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/apps/demo/test.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/backtest/__init__.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/backtest/__main__.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/backtest/metrics.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/backtest/replay.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/backtest/sim_match.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/base/__init__.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/base/console_utils.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/base/finance_decimal.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/base/market_buffer.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/base/market_utils.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/base/market_utils_nt.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/base/technical_analysis.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/base/telemetrics.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/base/trade_utils.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/engine/__init__.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/engine/algo_engine.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/engine/event_engine.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/engine/market_engine.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/engine/trade_engine.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/monitor/__init__.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/monitor/advanced_data_interface.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/profile/__init__.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/profile/cn.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/strategy/__init__.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/strategy/strategy_engine.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/utils/__init__.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/utils/commit_regularizer.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/algo_engine/utils/data_utils.py +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/setup.cfg +0 -0
- {pyalgoengine-0.7.5.post1 → pyalgoengine-0.7.6}/setup.py +0 -0
|
@@ -15,6 +15,10 @@ algo_engine/apps/backtest/tester.py
|
|
|
15
15
|
algo_engine/apps/backtest/web_app.py
|
|
16
16
|
algo_engine/apps/demo/__init__.py
|
|
17
17
|
algo_engine/apps/demo/test.py
|
|
18
|
+
algo_engine/apps/sim_input/__init__.py
|
|
19
|
+
algo_engine/apps/sim_input/sim_keyboard.py
|
|
20
|
+
algo_engine/apps/sim_input/sim_mouse.py
|
|
21
|
+
algo_engine/apps/sim_input/window.py
|
|
18
22
|
algo_engine/backtest/__init__.py
|
|
19
23
|
algo_engine/backtest/__main__.py
|
|
20
24
|
algo_engine/backtest/metrics.py
|
|
@@ -188,6 +188,7 @@ class DocManager(object):
|
|
|
188
188
|
port=self.bokeh_port,
|
|
189
189
|
check_unused_sessions_milliseconds=(self.bokeh_check_unused_sessions * 1000),
|
|
190
190
|
allow_websocket_origin=[f"{self.bokeh_host}:{self.bokeh_port}"] + websocket_origin,
|
|
191
|
+
use_xheaders=True,
|
|
191
192
|
# num_procs=1
|
|
192
193
|
)
|
|
193
194
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .. import LOGGER
|
|
2
|
+
|
|
3
|
+
import platform
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def check_windows_version(min_version=(6, 1)):
|
|
8
|
+
"""
|
|
9
|
+
Ensure the script is running on a compatible Windows version.
|
|
10
|
+
:param min_version: Minimum required version as a tuple (major, minor).
|
|
11
|
+
Default is (6, 1) for Windows 7 (major=6, minor=1).
|
|
12
|
+
"""
|
|
13
|
+
# Get the Windows version
|
|
14
|
+
if not platform.system() == "Windows":
|
|
15
|
+
raise EnvironmentError("This script only works on Windows.")
|
|
16
|
+
|
|
17
|
+
version_str = platform.version()
|
|
18
|
+
version_tuple = tuple(map(int, platform.win32_ver()[1].split('.')))
|
|
19
|
+
|
|
20
|
+
if version_tuple < min_version:
|
|
21
|
+
raise EnvironmentError(f"Unsupported Windows version: {version_str}. Minimum required is {min_version[0]}.{min_version[1]}.")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
check_windows_version((6, 1))
|
|
25
|
+
|
|
26
|
+
LOGGER = LOGGER.getChild('SimInput')
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
__package__ = 'algo_engine.apps.sim_input'
|
|
2
|
+
|
|
3
|
+
import ctypes
|
|
4
|
+
import enum
|
|
5
|
+
import time
|
|
6
|
+
|
|
7
|
+
from . import LOGGER
|
|
8
|
+
|
|
9
|
+
LOGGER.getChild('Keyboard')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Constants for key event types
|
|
13
|
+
class KeyEvent(enum.IntEnum):
|
|
14
|
+
F_KEYDOWN = 0x0000
|
|
15
|
+
F_KEYUP = 0x0002
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Windows API functions
|
|
19
|
+
user32 = ctypes.windll.user32
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Map character to virtual key code and shift state
|
|
23
|
+
def get_keycode(char: str):
|
|
24
|
+
"""
|
|
25
|
+
Get the virtual key code (VK_CODE) and shift state for a given character.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
tuple: (VK_CODE, needs_shift)
|
|
29
|
+
"""
|
|
30
|
+
vk_combo = user32.VkKeyScanW(ord(char))
|
|
31
|
+
vk_code = vk_combo & 0xFF # Lower byte is VK_CODE
|
|
32
|
+
shift_state = (vk_combo >> 8) & 0xFF # Higher byte indicates shift state
|
|
33
|
+
needs_shift = shift_state & 0x01 # Check if Shift key is required
|
|
34
|
+
return vk_code, needs_shift
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Simulate key press
|
|
38
|
+
def press_key(vk_code):
|
|
39
|
+
"""Press a key using its VK_CODE."""
|
|
40
|
+
ctypes.windll.user32.keybd_event(vk_code, 0, KeyEvent.F_KEYDOWN, 0)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Simulate key release
|
|
44
|
+
def release_key(vk_code):
|
|
45
|
+
"""Release a key using its VK_CODE."""
|
|
46
|
+
ctypes.windll.user32.keybd_event(vk_code, 0, KeyEvent.F_KEYUP, 0)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# Advanced function to simulate a key press for a given duration
|
|
50
|
+
def simulate_keypress(char, ts=0.1):
|
|
51
|
+
"""
|
|
52
|
+
Simulate pressing a key for a given duration.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
char (str): The character to simulate key press for.
|
|
56
|
+
ts (float): The duration (in seconds) to hold the key. Default is 0.1s.
|
|
57
|
+
"""
|
|
58
|
+
vk_code, needs_shift = get_keycode(char) # Get VK_CODE and shift state
|
|
59
|
+
if vk_code == 0xFF:
|
|
60
|
+
raise ValueError(f"Invalid character '{char}'. Cannot map to a virtual key code.")
|
|
61
|
+
|
|
62
|
+
# If the character needs Shift, press Shift first
|
|
63
|
+
if needs_shift:
|
|
64
|
+
press_key(0x10) # VK_CODE for Shift
|
|
65
|
+
|
|
66
|
+
press_key(vk_code) # Press the key
|
|
67
|
+
time.sleep(ts) # Hold the key for the given duration
|
|
68
|
+
release_key(vk_code) # Release the key
|
|
69
|
+
|
|
70
|
+
# If Shift was pressed, release it
|
|
71
|
+
if needs_shift:
|
|
72
|
+
release_key(0x10) # VK_CODE for Shift
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def main():
|
|
76
|
+
LOGGER.info('Staring sim input sequence in 5 seconds...')
|
|
77
|
+
time.sleep(5)
|
|
78
|
+
key_strikes = list('hellow world!')
|
|
79
|
+
for char in key_strikes:
|
|
80
|
+
LOGGER.info(f'Pressing {char}...')
|
|
81
|
+
simulate_keypress(char=char, ts=.5)
|
|
82
|
+
time.sleep(.5)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Example usage
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
main()
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
__package__ = 'algo_engine.apps.sim_input'
|
|
2
|
+
|
|
3
|
+
import ctypes
|
|
4
|
+
import enum
|
|
5
|
+
import time
|
|
6
|
+
from ctypes import wintypes
|
|
7
|
+
from typing import Literal
|
|
8
|
+
|
|
9
|
+
from . import LOGGER
|
|
10
|
+
|
|
11
|
+
LOGGER.getChild('Mouse')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Constants for mouse events
|
|
15
|
+
class MouseEvent(enum.IntEnum):
|
|
16
|
+
MOUSEEVENTF_MOVE = 0x0001
|
|
17
|
+
MOUSEEVENTF_LEFTDOWN = 0x0002
|
|
18
|
+
MOUSEEVENTF_LEFTUP = 0x0004
|
|
19
|
+
MOUSEEVENTF_RIGHTDOWN = 0x0008
|
|
20
|
+
MOUSEEVENTF_RIGHTUP = 0x0010
|
|
21
|
+
MOUSEEVENTF_ABSOLUTE = 0x8000
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Structure to store point coordinates
|
|
25
|
+
class POINT(ctypes.Structure):
|
|
26
|
+
_fields_ = [("x", wintypes.LONG), ("y", wintypes.LONG)]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Windows API functions
|
|
30
|
+
user32 = ctypes.windll.user32
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# Get the current mouse location
|
|
34
|
+
def get_location() -> POINT:
|
|
35
|
+
"""
|
|
36
|
+
Get the current mouse cursor position.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
tuple: (x, y) coordinates of the mouse cursor.
|
|
40
|
+
"""
|
|
41
|
+
point = POINT()
|
|
42
|
+
user32.GetCursorPos(ctypes.byref(point))
|
|
43
|
+
return point
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def move_mouse(x: int, y: int):
|
|
47
|
+
"""
|
|
48
|
+
Move the mouse cursor to a specific screen location.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
x (int): The target x-coordinate.
|
|
52
|
+
y (int): The target y-coordinate.
|
|
53
|
+
"""
|
|
54
|
+
user32.SetCursorPos(x, y)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Simulate mouse press
|
|
58
|
+
def press_mouse(button: Literal['l', 'r', 'left', 'right'] = 'l'):
|
|
59
|
+
"""
|
|
60
|
+
Simulate a mouse button press.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
button (str): The button to press ("left" or "right").
|
|
64
|
+
"""
|
|
65
|
+
match button:
|
|
66
|
+
case 'l' | "left":
|
|
67
|
+
user32.mouse_event(MouseEvent.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
|
|
68
|
+
case 'r' | "right":
|
|
69
|
+
user32.mouse_event(MouseEvent.MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0)
|
|
70
|
+
case _:
|
|
71
|
+
raise ValueError("Invalid button. Use 'left' or 'right'.")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# Simulate mouse release
|
|
75
|
+
def release_mouse(button: Literal['l', 'r', 'left', 'right'] = 'l'):
|
|
76
|
+
"""
|
|
77
|
+
Simulate a mouse button release.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
button (str): The button to release ("left" or "right").
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
match button:
|
|
84
|
+
case 'l' | "left":
|
|
85
|
+
user32.mouse_event(MouseEvent.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
|
|
86
|
+
case 'r' | "right":
|
|
87
|
+
user32.mouse_event(MouseEvent.MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0)
|
|
88
|
+
case _:
|
|
89
|
+
raise ValueError("Invalid button. Use 'left' or 'right'.")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# Simulate a mouse click
|
|
93
|
+
def click_mouse(button: Literal['l', 'r', 'left', 'right'] = 'l'):
|
|
94
|
+
"""
|
|
95
|
+
Simulate a mouse click (press + release).
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
button (str): The button to click ("left" or "right").
|
|
99
|
+
"""
|
|
100
|
+
press_mouse(button)
|
|
101
|
+
release_mouse(button)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# Simulate a mouse double click
|
|
105
|
+
def double_click_mouse(button: Literal['l', 'r', 'left', 'right'] = 'l', interval: float = 0.1):
|
|
106
|
+
"""
|
|
107
|
+
Simulate a mouse double click.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
button (str): The button to double-click ("left" or "right").
|
|
111
|
+
interval (float): Time between clicks (in seconds). Default is 0.1s.
|
|
112
|
+
"""
|
|
113
|
+
click_mouse(button)
|
|
114
|
+
time.sleep(interval)
|
|
115
|
+
click_mouse(button)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# Example usage
|
|
119
|
+
if __name__ == "__main__":
|
|
120
|
+
LOGGER.info('Staring sim mouse input sequence in 5 seconds...')
|
|
121
|
+
time.sleep(5)
|
|
122
|
+
|
|
123
|
+
p = get_location()
|
|
124
|
+
LOGGER.info(f"Current mouse location: {p.x, p.y}")
|
|
125
|
+
|
|
126
|
+
LOGGER.info('Move to diagonal location...')
|
|
127
|
+
move_mouse(x=p.y, y=p.x)
|
|
128
|
+
|
|
129
|
+
LOGGER.info(f"Simulating left click...")
|
|
130
|
+
click_mouse(button="l")
|
|
131
|
+
|
|
132
|
+
LOGGER.info(f"Simulating right click...")
|
|
133
|
+
click_mouse(button="r")
|
|
134
|
+
|
|
135
|
+
LOGGER.info("Simulating double click...")
|
|
136
|
+
double_click_mouse()
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
__package__ = 'algo_engine.apps.sim_input'
|
|
2
|
+
|
|
3
|
+
import ctypes
|
|
4
|
+
import dataclasses
|
|
5
|
+
import enum
|
|
6
|
+
import os
|
|
7
|
+
from typing import Literal
|
|
8
|
+
|
|
9
|
+
from . import LOGGER
|
|
10
|
+
|
|
11
|
+
LOGGER.getChild('Window')
|
|
12
|
+
|
|
13
|
+
# Define necessary Windows API functions and constants
|
|
14
|
+
user32 = ctypes.windll.user32
|
|
15
|
+
kernel32 = ctypes.windll.kernel32
|
|
16
|
+
psapi = ctypes.windll.psapi
|
|
17
|
+
|
|
18
|
+
# Define constants for access rights
|
|
19
|
+
PROCESS_QUERY_INFORMATION = 0x0400
|
|
20
|
+
PROCESS_VM_READ = 0x0010
|
|
21
|
+
|
|
22
|
+
# Define necessary Windows API function prototypes
|
|
23
|
+
WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_int, ctypes.POINTER(ctypes.c_int))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclasses.dataclass(frozen=True)
|
|
27
|
+
class WindowInfo:
|
|
28
|
+
window_name: str
|
|
29
|
+
pid: int
|
|
30
|
+
hwnd: int
|
|
31
|
+
executable_name: str
|
|
32
|
+
executable_path: str
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class WindowState(enum.IntEnum):
|
|
36
|
+
SW_SHOWNORMAL = 1 # Show the window normally
|
|
37
|
+
SW_SHOWMINIMIZED = 2 # Minimize the window
|
|
38
|
+
SW_SHOWMAXIMIZED = 3 # Maximize the window
|
|
39
|
+
SW_SHOWNOACTIVATE = 4 # Show the window without activating it
|
|
40
|
+
SW_SHOW = 5 # Show the window and bring it to the foreground
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Function to get PID from window handle
|
|
44
|
+
def get_pid(hwnd):
|
|
45
|
+
pid = ctypes.c_ulong()
|
|
46
|
+
user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid))
|
|
47
|
+
return pid.value
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Function to retrieve the executable path and name of a process
|
|
51
|
+
def get_executable_info(pid):
|
|
52
|
+
# Open the process to get information
|
|
53
|
+
h_process = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid)
|
|
54
|
+
if not h_process:
|
|
55
|
+
return None, None
|
|
56
|
+
|
|
57
|
+
# Buffer to hold the path
|
|
58
|
+
path_buffer = ctypes.create_unicode_buffer(1024)
|
|
59
|
+
|
|
60
|
+
# Get the full executable path using GetModuleFileNameEx
|
|
61
|
+
if psapi.GetModuleFileNameExW(h_process, 0, path_buffer, ctypes.byref(ctypes.c_ulong(1024))):
|
|
62
|
+
executable_path = path_buffer.value
|
|
63
|
+
executable_name = os.path.basename(executable_path)
|
|
64
|
+
else:
|
|
65
|
+
executable_path = None
|
|
66
|
+
executable_name = None
|
|
67
|
+
|
|
68
|
+
# Close the process handle
|
|
69
|
+
kernel32.CloseHandle(h_process)
|
|
70
|
+
|
|
71
|
+
return executable_name, executable_path
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# Function to check if a window is visible (including minimized)
|
|
75
|
+
def is_window_visible(hwnd):
|
|
76
|
+
return user32.IsWindowVisible(hwnd)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# Function to enumerate windows
|
|
80
|
+
def get_windows() -> list[WindowInfo]:
|
|
81
|
+
windows = []
|
|
82
|
+
|
|
83
|
+
def enum_windows_proc(hwnd, _):
|
|
84
|
+
# Only include visible windows
|
|
85
|
+
if not is_window_visible(hwnd):
|
|
86
|
+
return True
|
|
87
|
+
|
|
88
|
+
# Get window title (for name)
|
|
89
|
+
length = user32.GetWindowTextLengthW(hwnd)
|
|
90
|
+
if length > 0:
|
|
91
|
+
buffer = ctypes.create_unicode_buffer(length + 1)
|
|
92
|
+
user32.GetWindowTextW(hwnd, buffer, length + 1)
|
|
93
|
+
window_name = buffer.value
|
|
94
|
+
else:
|
|
95
|
+
window_name = "Untitled"
|
|
96
|
+
|
|
97
|
+
# Get the PID for the window
|
|
98
|
+
pid = get_pid(hwnd)
|
|
99
|
+
|
|
100
|
+
# Get executable name and path
|
|
101
|
+
executable_name, executable_path = get_executable_info(pid)
|
|
102
|
+
|
|
103
|
+
# Append to the windows list as a tuple (window_name, pid, hwnd, executable_name, executable_path)
|
|
104
|
+
windows.append(WindowInfo(window_name=window_name, pid=pid, hwnd=hwnd, executable_name=executable_name, executable_path=executable_path))
|
|
105
|
+
return True
|
|
106
|
+
|
|
107
|
+
# Enumerate all windows (including child windows)
|
|
108
|
+
user32.EnumWindows(WNDENUMPROC(enum_windows_proc), 0)
|
|
109
|
+
|
|
110
|
+
return windows
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def find_window(name: str = None, executable: str = None) -> list[WindowInfo]:
|
|
114
|
+
windows = get_windows()
|
|
115
|
+
matched = []
|
|
116
|
+
|
|
117
|
+
for window in windows:
|
|
118
|
+
if name is not None and name.lower() not in window.name.lower():
|
|
119
|
+
continue
|
|
120
|
+
|
|
121
|
+
if executable is not None and executable.lower() not in window.executable_name.lower():
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
matched.append(window)
|
|
125
|
+
|
|
126
|
+
return matched
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# Function to set the window action (top, maximize, minimize)
|
|
130
|
+
def set_window(window_info: WindowInfo, action: Literal['top', 'maximize', 'minimize', 'max', 'min']):
|
|
131
|
+
hwnd = window_info.hwnd
|
|
132
|
+
|
|
133
|
+
match action:
|
|
134
|
+
case "top":
|
|
135
|
+
# Bring the window to the front (top)
|
|
136
|
+
user32.ShowWindow(hwnd, WindowState.SW_SHOWNORMAL)
|
|
137
|
+
user32.SetForegroundWindow(hwnd)
|
|
138
|
+
case "maximize" | 'max':
|
|
139
|
+
# Maximize the window
|
|
140
|
+
user32.ShowWindow(hwnd, WindowState.SW_SHOWMAXIMIZED)
|
|
141
|
+
case "minimize" | 'min':
|
|
142
|
+
# Minimize the window
|
|
143
|
+
user32.ShowWindow(hwnd, WindowState.SW_SHOWMINIMIZED)
|
|
144
|
+
case _:
|
|
145
|
+
raise ValueError(f"Unknown action: {action}")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def main():
|
|
149
|
+
# Example usage
|
|
150
|
+
windows = get_windows()
|
|
151
|
+
for _ in windows:
|
|
152
|
+
LOGGER.debug(_)
|
|
153
|
+
|
|
154
|
+
firefox = find_window(executable="firefox")[0]
|
|
155
|
+
LOGGER.info(firefox)
|
|
156
|
+
|
|
157
|
+
set_window(window_info=firefox, action='top')
|
|
158
|
+
set_window(window_info=firefox, action='maximize')
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
if __name__ == "__main__":
|
|
162
|
+
main()
|
|
@@ -1379,7 +1379,7 @@ class OrderBook(MarketData):
|
|
|
1379
1379
|
float: The best bid volume, or NaN if not available.
|
|
1380
1380
|
"""
|
|
1381
1381
|
if book := self.bid:
|
|
1382
|
-
return book
|
|
1382
|
+
return book.at_level(0)[1]
|
|
1383
1383
|
else:
|
|
1384
1384
|
return math.nan
|
|
1385
1385
|
|
|
@@ -1392,7 +1392,7 @@ class OrderBook(MarketData):
|
|
|
1392
1392
|
float: The best ask volume, or NaN if not available.
|
|
1393
1393
|
"""
|
|
1394
1394
|
if book := self.ask:
|
|
1395
|
-
return book
|
|
1395
|
+
return book.at_level(0)[1]
|
|
1396
1396
|
else:
|
|
1397
1397
|
return math.nan
|
|
1398
1398
|
|
|
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.5.post1 → pyalgoengine-0.7.6}/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
|