hubble-futures 0.2.13__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.
- hubble_futures/__init__.py +151 -0
- hubble_futures/aster.py +601 -0
- hubble_futures/base.py +430 -0
- hubble_futures/config.py +34 -0
- hubble_futures/function_log.py +303 -0
- hubble_futures/version.py +8 -0
- hubble_futures/weex.py +1246 -0
- hubble_futures-0.2.13.dist-info/METADATA +217 -0
- hubble_futures-0.2.13.dist-info/RECORD +11 -0
- hubble_futures-0.2.13.dist-info/WHEEL +4 -0
- hubble_futures-0.2.13.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Structured Function Call Logging Module
|
|
3
|
+
|
|
4
|
+
This module provides structured logging for function calls and results in container agents.
|
|
5
|
+
It tracks all trading-related function calls with timing, parameters, and results.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from hubble_futures import start_function_log, record_function_call, finish_function_call, export_function_log
|
|
9
|
+
|
|
10
|
+
# Start logging at container entry
|
|
11
|
+
start_function_log()
|
|
12
|
+
|
|
13
|
+
# Record function calls
|
|
14
|
+
record_function_call("open_position", {"symbol": "BTCUSDT", "side": "BUY"})
|
|
15
|
+
finish_function_call("open_position", {"order_id": "12345", "status": "filled"})
|
|
16
|
+
|
|
17
|
+
# Export and clear at container exit
|
|
18
|
+
result = export_function_log(clear=True)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import contextvars
|
|
22
|
+
import copy
|
|
23
|
+
import time
|
|
24
|
+
from datetime import datetime
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Context variable for function log storage (async-safe)
|
|
29
|
+
_log_storage: contextvars.ContextVar[dict] = contextvars.ContextVar("_function_log_storage")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _create_new_storage() -> dict:
|
|
33
|
+
"""Create a new log storage instance with fresh mutable containers."""
|
|
34
|
+
return {
|
|
35
|
+
"function_calls": [],
|
|
36
|
+
"trading_summary": {},
|
|
37
|
+
"warnings": [],
|
|
38
|
+
"errors": [],
|
|
39
|
+
"metadata": {
|
|
40
|
+
"start_time": None,
|
|
41
|
+
"end_time": None,
|
|
42
|
+
"container_id": None,
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _get_log_storage() -> dict:
|
|
48
|
+
"""
|
|
49
|
+
Get context-local log storage.
|
|
50
|
+
|
|
51
|
+
Returns the storage for the current context. Must call start_function_log()
|
|
52
|
+
before this to initialize the storage.
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
LookupError: If start_function_log() has not been called
|
|
56
|
+
"""
|
|
57
|
+
return _log_storage.get()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def start_function_log(container_id: str | None = None) -> None:
|
|
61
|
+
"""
|
|
62
|
+
Initialize function logging for the current container execution.
|
|
63
|
+
|
|
64
|
+
Must be called at the container entry point before any function calls.
|
|
65
|
+
Creates a fresh storage instance to avoid sharing state across async tasks.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
container_id: Optional container identifier for tracking
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
start_function_log(container_id="agent-abc-123")
|
|
72
|
+
"""
|
|
73
|
+
# Create a fresh storage instance for this context
|
|
74
|
+
storage = _create_new_storage()
|
|
75
|
+
storage["metadata"]["start_time"] = datetime.utcnow().isoformat()
|
|
76
|
+
storage["metadata"]["container_id"] = container_id
|
|
77
|
+
_log_storage.set(storage)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def record_function_call(function_name: str, parameters: dict[str, Any]) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Record the start of a function call with parameters.
|
|
83
|
+
|
|
84
|
+
Call this immediately before invoking a trading function.
|
|
85
|
+
Silently does nothing if logging has not been started (non-container scenarios).
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
function_name: Name of the function being called (e.g., "open_position")
|
|
89
|
+
parameters: Parameters passed to the function
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
record_function_call("open_position", {"symbol": "BTCUSDT", "side": "BUY", "amount": 0.001})
|
|
93
|
+
"""
|
|
94
|
+
try:
|
|
95
|
+
storage = _get_log_storage()
|
|
96
|
+
except LookupError:
|
|
97
|
+
# Logging not initialized (non-container scenario), silently ignore
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
call_record = {
|
|
101
|
+
"function": function_name,
|
|
102
|
+
"parameters": parameters,
|
|
103
|
+
"start_time": datetime.utcnow().isoformat(),
|
|
104
|
+
"end_time": None,
|
|
105
|
+
"duration_ms": None,
|
|
106
|
+
"result": None,
|
|
107
|
+
"error": None,
|
|
108
|
+
"status": "pending",
|
|
109
|
+
}
|
|
110
|
+
storage["function_calls"].append(call_record)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def finish_function_call(function_name: str, result: dict[str, Any] | None = None, error: str | None = None) -> None:
|
|
114
|
+
"""
|
|
115
|
+
Record the completion of a function call with result or error.
|
|
116
|
+
|
|
117
|
+
Call this immediately after a trading function returns.
|
|
118
|
+
Silently does nothing if logging has not been started (non-container scenarios).
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
function_name: Name of the function that completed (must match record_function_call)
|
|
122
|
+
result: Function return value (e.g., {"order_id": "12345", "status": "filled"})
|
|
123
|
+
error: Error message if the function failed
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
# Success case
|
|
127
|
+
finish_function_call("open_position", {"order_id": "12345", "status": "filled"})
|
|
128
|
+
|
|
129
|
+
# Error case
|
|
130
|
+
finish_function_call("open_position", error="Insufficient margin")
|
|
131
|
+
"""
|
|
132
|
+
try:
|
|
133
|
+
storage = _get_log_storage()
|
|
134
|
+
except LookupError:
|
|
135
|
+
# Logging not initialized (non-container scenario), silently ignore
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
end_time = datetime.utcnow().isoformat()
|
|
139
|
+
|
|
140
|
+
# Find the most recent pending call for this function
|
|
141
|
+
for call in reversed(storage["function_calls"]):
|
|
142
|
+
if call["function"] == function_name and call["status"] == "pending":
|
|
143
|
+
call["end_time"] = end_time
|
|
144
|
+
call["result"] = result
|
|
145
|
+
call["error"] = error
|
|
146
|
+
|
|
147
|
+
# Calculate duration
|
|
148
|
+
if call["start_time"]:
|
|
149
|
+
try:
|
|
150
|
+
start = datetime.fromisoformat(call["start_time"])
|
|
151
|
+
end = datetime.fromisoformat(end_time)
|
|
152
|
+
call["duration_ms"] = int((end - start).total_seconds() * 1000)
|
|
153
|
+
except Exception:
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
# Set status
|
|
157
|
+
if error:
|
|
158
|
+
call["status"] = "failed"
|
|
159
|
+
else:
|
|
160
|
+
call["status"] = "succeeded"
|
|
161
|
+
break
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def set_trading_summary(summary: dict[str, Any]) -> None:
|
|
165
|
+
"""
|
|
166
|
+
Set the overall trading summary for this execution.
|
|
167
|
+
|
|
168
|
+
Call this at the end of execution to summarize trading activity.
|
|
169
|
+
Silently does nothing if logging has not been started (non-container scenarios).
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
summary: Trading summary dict with keys like:
|
|
173
|
+
- executed: Total number of trades executed
|
|
174
|
+
- orders: List of order IDs
|
|
175
|
+
- final_position: Net position after all trades
|
|
176
|
+
- total_pnl: Total profit/loss
|
|
177
|
+
|
|
178
|
+
Example:
|
|
179
|
+
set_trading_summary({
|
|
180
|
+
"executed": 2,
|
|
181
|
+
"orders": ["12345", "12346"],
|
|
182
|
+
"final_position": {"BTCUSDT": 0.001},
|
|
183
|
+
"total_pnl": 10.50,
|
|
184
|
+
})
|
|
185
|
+
"""
|
|
186
|
+
try:
|
|
187
|
+
storage = _get_log_storage()
|
|
188
|
+
except LookupError:
|
|
189
|
+
# Logging not initialized (non-container scenario), silently ignore
|
|
190
|
+
return
|
|
191
|
+
storage["trading_summary"] = summary
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def add_warning(message: str) -> None:
|
|
195
|
+
"""
|
|
196
|
+
Add a warning message to the log.
|
|
197
|
+
Silently does nothing if logging has not been started (non-container scenarios).
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
message: Warning message to record
|
|
201
|
+
"""
|
|
202
|
+
try:
|
|
203
|
+
storage = _get_log_storage()
|
|
204
|
+
except LookupError:
|
|
205
|
+
# Logging not initialized (non-container scenario), silently ignore
|
|
206
|
+
return
|
|
207
|
+
storage["warnings"].append({
|
|
208
|
+
"message": message,
|
|
209
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def add_error(message: str) -> None:
|
|
214
|
+
"""
|
|
215
|
+
Add an error message to the log.
|
|
216
|
+
Silently does nothing if logging has not been started (non-container scenarios).
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
message: Error message to record
|
|
220
|
+
"""
|
|
221
|
+
try:
|
|
222
|
+
storage = _get_log_storage()
|
|
223
|
+
except LookupError:
|
|
224
|
+
# Logging not initialized (non-container scenario), silently ignore
|
|
225
|
+
return
|
|
226
|
+
storage["errors"].append({
|
|
227
|
+
"message": message,
|
|
228
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def export_function_log(clear: bool = True) -> dict[str, Any]:
|
|
233
|
+
"""
|
|
234
|
+
Export the current function log as a structured JSON dict.
|
|
235
|
+
|
|
236
|
+
Call this at container exit to get the complete log.
|
|
237
|
+
Returns a deep copy to prevent external modifications from affecting internal state.
|
|
238
|
+
Returns empty log if logging has not been started.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
clear: If True, clear the log after exporting (default: True)
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
Structured log dict with keys:
|
|
245
|
+
- metadata: Execution metadata (start_time, end_time, container_id)
|
|
246
|
+
- function_calls: List of all function calls with timing and results
|
|
247
|
+
- trading_summary: Trading summary (if set)
|
|
248
|
+
- warnings: List of warnings
|
|
249
|
+
- errors: List of errors
|
|
250
|
+
|
|
251
|
+
Example:
|
|
252
|
+
result = export_function_log(clear=True)
|
|
253
|
+
# Returns: {"metadata": {...}, "function_calls": [...], ...}
|
|
254
|
+
"""
|
|
255
|
+
try:
|
|
256
|
+
storage = _get_log_storage()
|
|
257
|
+
storage["metadata"]["end_time"] = datetime.utcnow().isoformat()
|
|
258
|
+
|
|
259
|
+
# Build deep copy to avoid external modifications
|
|
260
|
+
export = copy.deepcopy({
|
|
261
|
+
"metadata": storage["metadata"],
|
|
262
|
+
"function_calls": storage["function_calls"],
|
|
263
|
+
"trading_summary": storage["trading_summary"],
|
|
264
|
+
"warnings": storage["warnings"],
|
|
265
|
+
"errors": storage["errors"],
|
|
266
|
+
})
|
|
267
|
+
except LookupError:
|
|
268
|
+
# Logging not initialized, return empty log
|
|
269
|
+
export = copy.deepcopy(_create_new_storage())
|
|
270
|
+
|
|
271
|
+
# Clear if requested (create fresh mutable containers)
|
|
272
|
+
if clear:
|
|
273
|
+
_log_storage.set(_create_new_storage())
|
|
274
|
+
|
|
275
|
+
return export
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def get_function_log() -> dict[str, Any]:
|
|
279
|
+
"""
|
|
280
|
+
Get the current function log without clearing it.
|
|
281
|
+
Returns a deep copy to prevent external modifications from affecting internal state.
|
|
282
|
+
Returns empty log if logging has not been started.
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
Current log state (same structure as export_function_log)
|
|
286
|
+
"""
|
|
287
|
+
try:
|
|
288
|
+
storage = _get_log_storage()
|
|
289
|
+
return copy.deepcopy({
|
|
290
|
+
"metadata": storage["metadata"],
|
|
291
|
+
"function_calls": storage["function_calls"],
|
|
292
|
+
"trading_summary": storage["trading_summary"],
|
|
293
|
+
"warnings": storage["warnings"],
|
|
294
|
+
"errors": storage["errors"],
|
|
295
|
+
})
|
|
296
|
+
except LookupError:
|
|
297
|
+
# Logging not initialized, return empty log
|
|
298
|
+
return copy.deepcopy(_create_new_storage())
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def clear_function_log() -> None:
|
|
302
|
+
"""Clear all function log data by creating a fresh storage instance."""
|
|
303
|
+
_log_storage.set(_create_new_storage())
|