cnhkmcp 2.0.3__py3-none-any.whl → 2.1.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.
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/README.md +38 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/config.json +6 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/get_knowledgeBase_tool/ace_lib.py +1510 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/get_knowledgeBase_tool/fetch_all_datasets.py +157 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/get_knowledgeBase_tool/fetch_all_documentation.py +132 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/get_knowledgeBase_tool/fetch_all_operators.py +99 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/get_knowledgeBase_tool/helpful_functions.py +180 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/icon.ico +0 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/icon.png +0 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/knowledge/test.txt +1 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/main.py +581 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/process_knowledge_base.py +280 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/rag_engine.py +265 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/requirements.txt +12 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/run.bat +3 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/vector_db/chroma.sqlite3 +0 -0
- cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242//321/211/320/266/320/246/321/206/320/274/320/261/321/210/342/224/220/320/240/321/210/320/261/320/234/321/206/320/231/320/243/321/205/342/225/235/320/220/321/206/320/230/320/241.py +265 -0
- cnhkmcp/untracked/APP/Tranformer/Transformer.py +2804 -11
- cnhkmcp/untracked/APP/Tranformer/output/Alpha_candidates.json +1524 -889
- cnhkmcp/untracked/APP/Tranformer/output/Alpha_generated_expressions_error.json +884 -111
- cnhkmcp/untracked/APP/Tranformer/output/Alpha_generated_expressions_success.json +442 -168
- cnhkmcp/untracked/APP/Tranformer/template_summary.txt +2775 -1
- cnhkmcp/untracked/APP/ace.log +2 -0
- cnhkmcp/untracked/APP/give_me_idea/fetch_all_datasets.py +157 -0
- cnhkmcp/untracked/APP/give_me_idea/fetch_all_operators.py +99 -0
- cnhkmcp/untracked/APP/simulator/simulator_wqb.py +16 -16
- cnhkmcp/untracked/APP/static/brain.js +61 -0
- cnhkmcp/untracked/APP/static/script.js +140 -0
- cnhkmcp/untracked/APP/templates/index.html +25 -4
- cnhkmcp/untracked/APP//321/210/342/224/220/320/240/321/210/320/261/320/234/321/206/320/231/320/243/321/205/342/225/235/320/220/321/206/320/230/320/241.py +70 -8
- {cnhkmcp-2.0.3.dist-info → cnhkmcp-2.1.0.dist-info}/METADATA +1 -1
- {cnhkmcp-2.0.3.dist-info → cnhkmcp-2.1.0.dist-info}/RECORD +36 -20
- cnhkmcp/untracked/APP/hkSimulator/ace.log +0 -0
- cnhkmcp/untracked/APP/hkSimulator/autosim_20251205_145240.log +0 -0
- cnhkmcp/untracked/APP/hkSimulator/autosim_20251215_030103.log +0 -0
- {cnhkmcp-2.0.3.dist-info → cnhkmcp-2.1.0.dist-info}/WHEEL +0 -0
- {cnhkmcp-2.0.3.dist-info → cnhkmcp-2.1.0.dist-info}/entry_points.txt +0 -0
- {cnhkmcp-2.0.3.dist-info → cnhkmcp-2.1.0.dist-info}/licenses/LICENSE +0 -0
- {cnhkmcp-2.0.3.dist-info → cnhkmcp-2.1.0.dist-info}/top_level.txt +0 -0
cnhkmcp/untracked/APP/ace.log
CHANGED
|
@@ -65,3 +65,5 @@ Incorrect email or password
|
|
|
65
65
|
2025-12-18 02:52:11,310 - ace - WARNING - No fields found: region=CHN, delay=1, universe=TOP2000U, type=MATRIX, dataset.id=analyst45
|
|
66
66
|
2025-12-18 13:17:02,856 - ace - WARNING - No fields found: region=USA, delay=1, universe=TOP3000, type=MATRIX, dataset.id=shortinterest3
|
|
67
67
|
2025-12-18 13:24:08,699 - ace - WARNING - No fields found: region=USA, delay=1, universe=TOP3000, type=MATRIX, dataset.id=shortinterest3
|
|
68
|
+
2025-12-24 02:44:57,254 - ace - ERROR - Simulation failed. {'id': '3fryaq7y750HbRmjNtbF3x8', 'type': 'REGULAR', 'status': 'ERROR', 'message': 'Your simulation has been running too long. If you are running simulations in batch, consider to reduce number of concurrent simulations or input data.'}
|
|
69
|
+
2025-12-24 03:14:08,010 - ace - ERROR - Simulation failed. {'id': '1ZdRKEal75hUamZOV5ZRpTt', 'type': 'REGULAR', 'status': 'ERROR', 'message': 'Your simulation has been running too long. If you are running simulations in batch, consider to reduce number of concurrent simulations or input data.'}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import getpass
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
# Ensure we can import ace_lib from the project root
|
|
10
|
+
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
11
|
+
ROOT_DIR = os.path.dirname(SCRIPT_DIR)
|
|
12
|
+
if ROOT_DIR not in sys.path:
|
|
13
|
+
sys.path.append(ROOT_DIR)
|
|
14
|
+
|
|
15
|
+
import ace_lib # noqa: E402
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def prompt_credentials() -> tuple[str, str]:
|
|
19
|
+
"""Prompt user for platform credentials."""
|
|
20
|
+
email = input("Enter BRAIN Email: ").strip()
|
|
21
|
+
while not email:
|
|
22
|
+
email = input("Email is required. Enter BRAIN Email: ").strip()
|
|
23
|
+
|
|
24
|
+
password = getpass.getpass("Enter BRAIN Password: ").strip()
|
|
25
|
+
while not password:
|
|
26
|
+
password = getpass.getpass("Password is required. Enter BRAIN Password: ").strip()
|
|
27
|
+
|
|
28
|
+
return email, password
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def fetch_all_combinations(session: ace_lib.SingleSession) -> pd.DataFrame:
|
|
32
|
+
"""Return all valid instrument/region/delay/universe combos from platform settings."""
|
|
33
|
+
options_df = ace_lib.get_instrument_type_region_delay(session)
|
|
34
|
+
if options_df is None or options_df.empty:
|
|
35
|
+
raise RuntimeError("No simulation options fetched; cannot enumerate datasets.")
|
|
36
|
+
return options_df
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def fetch_datasets_for_combo(
|
|
40
|
+
session: ace_lib.SingleSession,
|
|
41
|
+
instrument_type: str,
|
|
42
|
+
region: str,
|
|
43
|
+
delay: int,
|
|
44
|
+
universe: str,
|
|
45
|
+
) -> pd.DataFrame:
|
|
46
|
+
"""Fetch datasets for one combination (theme ALL to include both theme true/false)."""
|
|
47
|
+
df = ace_lib.get_datasets(
|
|
48
|
+
session,
|
|
49
|
+
instrument_type=instrument_type,
|
|
50
|
+
region=region,
|
|
51
|
+
delay=delay,
|
|
52
|
+
universe=universe,
|
|
53
|
+
theme="ALL",
|
|
54
|
+
)
|
|
55
|
+
if df is None:
|
|
56
|
+
return pd.DataFrame()
|
|
57
|
+
|
|
58
|
+
df = df.copy()
|
|
59
|
+
df["param_instrument_type"] = instrument_type
|
|
60
|
+
df["param_region"] = region
|
|
61
|
+
df["param_delay"] = delay
|
|
62
|
+
df["param_universe"] = universe
|
|
63
|
+
df["combo_key"] = df.apply(
|
|
64
|
+
lambda row: f"{instrument_type}-{region}-D{delay}-{universe}",
|
|
65
|
+
axis=1,
|
|
66
|
+
)
|
|
67
|
+
return df
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def merge_and_deduplicate(datasets: List[pd.DataFrame]) -> pd.DataFrame:
|
|
71
|
+
"""Merge fetched datasets and deduplicate by dataset id, keeping all combo metadata."""
|
|
72
|
+
combined = pd.concat([df for df in datasets if not df.empty], ignore_index=True)
|
|
73
|
+
if combined.empty:
|
|
74
|
+
return combined
|
|
75
|
+
|
|
76
|
+
# Aggregate availability combos per dataset id
|
|
77
|
+
availability = (
|
|
78
|
+
combined.groupby("id")["combo_key"]
|
|
79
|
+
.agg(lambda x: " | ".join(sorted(set(x))))
|
|
80
|
+
.rename("available_in")
|
|
81
|
+
.reset_index()
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Drop duplicate rows by dataset id, keep first occurrence of other columns
|
|
85
|
+
unique_df = combined.drop_duplicates(subset=["id"]).copy()
|
|
86
|
+
unique_df = unique_df.merge(availability, on="id", how="left")
|
|
87
|
+
|
|
88
|
+
# Sort for readability
|
|
89
|
+
sort_cols = [col for col in ["category", "subcategory", "id"] if col in unique_df.columns]
|
|
90
|
+
if sort_cols:
|
|
91
|
+
# Ensure sort keys are hashable/strings to avoid unhashable dict errors
|
|
92
|
+
for col in sort_cols:
|
|
93
|
+
unique_df[col] = unique_df[col].apply(
|
|
94
|
+
lambda v: v
|
|
95
|
+
if pd.isna(v) or isinstance(v, (int, float, str, bool))
|
|
96
|
+
else json.dumps(v, ensure_ascii=False, sort_keys=True)
|
|
97
|
+
)
|
|
98
|
+
unique_df = unique_df.sort_values(sort_cols).reset_index(drop=True)
|
|
99
|
+
|
|
100
|
+
return unique_df
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def main():
|
|
104
|
+
print("=== Fetch All BRAIN Datasets (all regions/universes/delays) ===")
|
|
105
|
+
|
|
106
|
+
email, password = prompt_credentials()
|
|
107
|
+
|
|
108
|
+
# Monkey-patch ace_lib credential retrieval so start_session uses provided credentials
|
|
109
|
+
ace_lib.get_credentials = lambda: (email, password)
|
|
110
|
+
|
|
111
|
+
print("Logging in...")
|
|
112
|
+
try:
|
|
113
|
+
session = ace_lib.start_session()
|
|
114
|
+
print("Login successful.")
|
|
115
|
+
except Exception as exc:
|
|
116
|
+
print(f"Login failed: {exc}")
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
print("Fetching valid instrument/region/delay/universe combinations from platform settings...")
|
|
120
|
+
try:
|
|
121
|
+
options_df = fetch_all_combinations(session)
|
|
122
|
+
except Exception as exc:
|
|
123
|
+
print(f"Failed to fetch simulation options: {exc}")
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
all_datasets: List[pd.DataFrame] = []
|
|
127
|
+
total_combos = 0
|
|
128
|
+
|
|
129
|
+
for _, row in options_df.iterrows():
|
|
130
|
+
instrument_type = row.get("InstrumentType")
|
|
131
|
+
region = row.get("Region")
|
|
132
|
+
delay = row.get("Delay")
|
|
133
|
+
universes = row.get("Universe") or []
|
|
134
|
+
|
|
135
|
+
for universe in universes:
|
|
136
|
+
total_combos += 1
|
|
137
|
+
print(f"[{total_combos}] Fetching datasets for {instrument_type} / {region} / D{delay} / {universe}...")
|
|
138
|
+
try:
|
|
139
|
+
df = fetch_datasets_for_combo(session, instrument_type, region, delay, universe)
|
|
140
|
+
print(f" -> Retrieved {len(df)} rows")
|
|
141
|
+
all_datasets.append(df)
|
|
142
|
+
except Exception as exc:
|
|
143
|
+
print(f" -> Failed for {instrument_type}-{region}-D{delay}-{universe}: {exc}")
|
|
144
|
+
|
|
145
|
+
result_df = merge_and_deduplicate(all_datasets)
|
|
146
|
+
|
|
147
|
+
if result_df.empty:
|
|
148
|
+
print("No datasets fetched; nothing to save.")
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
output_path = os.path.join(SCRIPT_DIR, "all_datasets_full.csv")
|
|
152
|
+
result_df.to_csv(output_path, index=False)
|
|
153
|
+
print(f"Saved {len(result_df)} unique datasets to {output_path}")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
if __name__ == "__main__":
|
|
157
|
+
main()
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import getpass
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
# Make ace_lib importable
|
|
9
|
+
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
10
|
+
ROOT_DIR = os.path.dirname(SCRIPT_DIR)
|
|
11
|
+
if ROOT_DIR not in sys.path:
|
|
12
|
+
sys.path.append(ROOT_DIR)
|
|
13
|
+
|
|
14
|
+
import ace_lib # noqa: E402
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def prompt_credentials() -> tuple[str, str]:
|
|
18
|
+
email = input("Enter BRAIN Email: ").strip()
|
|
19
|
+
while not email:
|
|
20
|
+
email = input("Email is required. Enter BRAIN Email: ").strip()
|
|
21
|
+
|
|
22
|
+
password = getpass.getpass("Enter BRAIN Password: ").strip()
|
|
23
|
+
while not password:
|
|
24
|
+
password = getpass.getpass("Password is required. Enter BRAIN Password: ").strip()
|
|
25
|
+
|
|
26
|
+
return email, password
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def fetch_operators(session: ace_lib.SingleSession) -> pd.DataFrame:
|
|
30
|
+
df = ace_lib.get_operators(session)
|
|
31
|
+
if df is None or df.empty:
|
|
32
|
+
return pd.DataFrame()
|
|
33
|
+
|
|
34
|
+
df = df.copy()
|
|
35
|
+
|
|
36
|
+
# Choose an identifier column robustly
|
|
37
|
+
id_col = "id" if "id" in df.columns else None
|
|
38
|
+
if id_col is None:
|
|
39
|
+
if "name" in df.columns:
|
|
40
|
+
id_col = "name"
|
|
41
|
+
else:
|
|
42
|
+
id_col = "_row_id"
|
|
43
|
+
df[id_col] = df.index
|
|
44
|
+
|
|
45
|
+
# Re-aggregate scopes so each operator id is unique
|
|
46
|
+
if "scope" in df.columns:
|
|
47
|
+
scope_map = (
|
|
48
|
+
df.groupby(id_col)["scope"]
|
|
49
|
+
.agg(lambda x: sorted(set([item for item in x if pd.notna(item)])))
|
|
50
|
+
.rename("scopes")
|
|
51
|
+
.reset_index()
|
|
52
|
+
)
|
|
53
|
+
else:
|
|
54
|
+
scope_map = pd.DataFrame({id_col: df[id_col].unique(), "scopes": [[] for _ in range(df[id_col].nunique())]})
|
|
55
|
+
|
|
56
|
+
unique_df = df.drop(columns=["scope"], errors="ignore").drop_duplicates(subset=[id_col]).merge(
|
|
57
|
+
scope_map, on=id_col, how="left"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Sort for readability
|
|
61
|
+
sort_cols: List[str] = [col for col in ["category", "subcategory", "name", id_col] if col in unique_df.columns]
|
|
62
|
+
if sort_cols:
|
|
63
|
+
unique_df = unique_df.sort_values(sort_cols).reset_index(drop=True)
|
|
64
|
+
|
|
65
|
+
return unique_df
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def main():
|
|
69
|
+
print("=== Fetch All BRAIN Operators ===")
|
|
70
|
+
|
|
71
|
+
email, password = prompt_credentials()
|
|
72
|
+
ace_lib.get_credentials = lambda: (email, password)
|
|
73
|
+
|
|
74
|
+
print("Logging in...")
|
|
75
|
+
try:
|
|
76
|
+
session = ace_lib.start_session()
|
|
77
|
+
print("Login successful.")
|
|
78
|
+
except Exception as exc:
|
|
79
|
+
print(f"Login failed: {exc}")
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
print("Fetching operators...")
|
|
83
|
+
try:
|
|
84
|
+
operators_df = fetch_operators(session)
|
|
85
|
+
except Exception as exc:
|
|
86
|
+
print(f"Failed to fetch operators: {exc}")
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
if operators_df.empty:
|
|
90
|
+
print("No operators returned; nothing to save.")
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
output_path = os.path.join(SCRIPT_DIR, "all_operators.csv")
|
|
94
|
+
operators_df.to_csv(output_path, index=False)
|
|
95
|
+
print(f"Saved {len(operators_df)} operators to {output_path}")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
main()
|
|
@@ -466,15 +466,15 @@ async def automated_main(json_file_content, username, password, start_position=0
|
|
|
466
466
|
print(f"成功完成 {len(resps)} 个回测")
|
|
467
467
|
|
|
468
468
|
print("\nAlpha IDs:")
|
|
469
|
-
for i, resp in enumerate(resps):
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
469
|
+
# for i, resp in enumerate(resps):
|
|
470
|
+
# try:
|
|
471
|
+
# alpha_id = resp.json()['alpha']
|
|
472
|
+
# alpha_ids.append(alpha_id)
|
|
473
|
+
# successful_count += 1
|
|
474
|
+
# print(f" {i+1:4d}. {alpha_id}")
|
|
475
|
+
# except Exception as e:
|
|
476
|
+
# failed_count += 1
|
|
477
|
+
# print(f" {i+1:4d}. 错误: {e}")
|
|
478
478
|
|
|
479
479
|
print("\n✅ 处理完成!")
|
|
480
480
|
|
|
@@ -597,13 +597,13 @@ async def main():
|
|
|
597
597
|
else:
|
|
598
598
|
print(f"成功完成 {len(resps)} 个回测")
|
|
599
599
|
|
|
600
|
-
print("\nAlpha IDs:")
|
|
601
|
-
for i, resp in enumerate(resps):
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
600
|
+
# print("\nAlpha IDs:")
|
|
601
|
+
# for i, resp in enumerate(resps):
|
|
602
|
+
# try:
|
|
603
|
+
# alpha_id = resp.json()['alpha']
|
|
604
|
+
# print(f" {i+1:4d}. {alpha_id}")
|
|
605
|
+
# except Exception as e:
|
|
606
|
+
# print(f" {i+1:4d}. 错误: {e}")
|
|
607
607
|
|
|
608
608
|
except KeyboardInterrupt:
|
|
609
609
|
print("\n\n⚠️ 回测被用户中断")
|
|
@@ -304,6 +304,65 @@ async function getUserOperators() {
|
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
// Get data fields via proxy server
|
|
307
|
+
async function getDatasets(region = 'USA', delay = 1, universe = 'TOP3000') {
|
|
308
|
+
if (!brainSession || !brainSessionId) {
|
|
309
|
+
throw new Error('Not authenticated with BRAIN');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
try {
|
|
313
|
+
const params = new URLSearchParams({
|
|
314
|
+
region: region,
|
|
315
|
+
delay: delay.toString(),
|
|
316
|
+
universe: universe
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const response = await fetch(`${PROXY_BASE}/api/datasets?${params}`, {
|
|
320
|
+
method: 'GET',
|
|
321
|
+
headers: {
|
|
322
|
+
'Session-ID': brainSessionId
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
if (!response.ok) {
|
|
327
|
+
const errorData = await response.json();
|
|
328
|
+
throw new Error(errorData.error || 'Failed to fetch datasets');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const data = await response.json();
|
|
332
|
+
return data.results || [];
|
|
333
|
+
|
|
334
|
+
} catch (error) {
|
|
335
|
+
console.error('Failed to fetch datasets:', error);
|
|
336
|
+
throw error;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async function getSimulationOptions() {
|
|
341
|
+
if (!brainSession || !brainSessionId) {
|
|
342
|
+
throw new Error('Not authenticated with BRAIN');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
try {
|
|
346
|
+
const response = await fetch(`${PROXY_BASE}/api/simulation-options`, {
|
|
347
|
+
method: 'GET',
|
|
348
|
+
headers: {
|
|
349
|
+
'Session-ID': brainSessionId
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
if (!response.ok) {
|
|
354
|
+
const errorData = await response.json();
|
|
355
|
+
throw new Error(errorData.error || 'Failed to fetch simulation options');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return await response.json();
|
|
359
|
+
|
|
360
|
+
} catch (error) {
|
|
361
|
+
console.error('Failed to fetch simulation options:', error);
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
307
366
|
async function getDataFields(region = 'USA', delay = 1, universe = 'TOP3000', datasetId = 'fundamental6') {
|
|
308
367
|
if (!brainSession || !brainSessionId) {
|
|
309
368
|
throw new Error('Not authenticated with BRAIN');
|
|
@@ -514,6 +573,8 @@ window.brainAPI = {
|
|
|
514
573
|
isConnectedToBrain,
|
|
515
574
|
getAllOperators,
|
|
516
575
|
getAllDataFields,
|
|
576
|
+
getDatasets,
|
|
577
|
+
getSimulationOptions,
|
|
517
578
|
getDataFields,
|
|
518
579
|
getOperatorsByCategory,
|
|
519
580
|
searchOperators,
|
|
@@ -2265,6 +2265,99 @@ function openBrainDataFieldsModal() {
|
|
|
2265
2265
|
setupDataFieldsModalEventListeners();
|
|
2266
2266
|
|
|
2267
2267
|
modal.style.display = 'block';
|
|
2268
|
+
|
|
2269
|
+
// Auto-load datasets if connected
|
|
2270
|
+
if (window.brainAPI && window.brainAPI.isConnectedToBrain()) {
|
|
2271
|
+
loadDatasets();
|
|
2272
|
+
loadSimulationOptions();
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
let brainSimulationOptions = null;
|
|
2277
|
+
|
|
2278
|
+
async function loadSimulationOptions() {
|
|
2279
|
+
if (brainSimulationOptions) return; // Already loaded
|
|
2280
|
+
|
|
2281
|
+
try {
|
|
2282
|
+
if (!window.brainAPI || !window.brainAPI.isConnectedToBrain()) {
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
brainSimulationOptions = await window.brainAPI.getSimulationOptions();
|
|
2287
|
+
|
|
2288
|
+
// Populate Region Select
|
|
2289
|
+
const regionSelect = document.getElementById('regionSelect');
|
|
2290
|
+
const instrumentType = 'EQUITY'; // Default
|
|
2291
|
+
|
|
2292
|
+
if (brainSimulationOptions && brainSimulationOptions[instrumentType]) {
|
|
2293
|
+
const regions = Object.keys(brainSimulationOptions[instrumentType]);
|
|
2294
|
+
|
|
2295
|
+
regionSelect.innerHTML = '<option value="">Select...</option>';
|
|
2296
|
+
regions.forEach(r => {
|
|
2297
|
+
const option = document.createElement('option');
|
|
2298
|
+
option.value = r;
|
|
2299
|
+
option.textContent = r;
|
|
2300
|
+
regionSelect.appendChild(option);
|
|
2301
|
+
});
|
|
2302
|
+
|
|
2303
|
+
regionSelect.style.display = 'block';
|
|
2304
|
+
|
|
2305
|
+
// Trigger updates if values are already set
|
|
2306
|
+
updateDelayOptions();
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
} catch (error) {
|
|
2310
|
+
console.error('Failed to load simulation options:', error);
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
function updateDelayOptions() {
|
|
2315
|
+
const region = document.getElementById('regionInput').value;
|
|
2316
|
+
const delaySelect = document.getElementById('delaySelect');
|
|
2317
|
+
const instrumentType = 'EQUITY';
|
|
2318
|
+
|
|
2319
|
+
if (!brainSimulationOptions || !brainSimulationOptions[instrumentType] || !brainSimulationOptions[instrumentType][region]) {
|
|
2320
|
+
delaySelect.style.display = 'none';
|
|
2321
|
+
return;
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
// The structure is brainSimulationOptions[instrumentType][region] = { delays: [], universes: [], ... }
|
|
2325
|
+
const delays = brainSimulationOptions[instrumentType][region].delays || [];
|
|
2326
|
+
|
|
2327
|
+
delaySelect.innerHTML = '<option value="">Select...</option>';
|
|
2328
|
+
delays.forEach(d => {
|
|
2329
|
+
const option = document.createElement('option');
|
|
2330
|
+
option.value = d;
|
|
2331
|
+
option.textContent = d;
|
|
2332
|
+
delaySelect.appendChild(option);
|
|
2333
|
+
});
|
|
2334
|
+
|
|
2335
|
+
delaySelect.style.display = 'block';
|
|
2336
|
+
updateUniverseOptions();
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
function updateUniverseOptions() {
|
|
2340
|
+
const region = document.getElementById('regionInput').value;
|
|
2341
|
+
const universeSelect = document.getElementById('universeSelect');
|
|
2342
|
+
const instrumentType = 'EQUITY';
|
|
2343
|
+
|
|
2344
|
+
if (!brainSimulationOptions || !brainSimulationOptions[instrumentType] || !brainSimulationOptions[instrumentType][region]) {
|
|
2345
|
+
universeSelect.style.display = 'none';
|
|
2346
|
+
return;
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
// Universe depends on Region, not Delay in this data structure
|
|
2350
|
+
const universes = brainSimulationOptions[instrumentType][region].universes || [];
|
|
2351
|
+
|
|
2352
|
+
universeSelect.innerHTML = '<option value="">Select...</option>';
|
|
2353
|
+
universes.forEach(u => {
|
|
2354
|
+
const option = document.createElement('option');
|
|
2355
|
+
option.value = u;
|
|
2356
|
+
option.textContent = u;
|
|
2357
|
+
universeSelect.appendChild(option);
|
|
2358
|
+
});
|
|
2359
|
+
|
|
2360
|
+
universeSelect.style.display = 'block';
|
|
2268
2361
|
}
|
|
2269
2362
|
|
|
2270
2363
|
function closeBrainDataFieldsModal() {
|
|
@@ -2274,6 +2367,49 @@ function closeBrainDataFieldsModal() {
|
|
|
2274
2367
|
updateSelectedDataFieldsDisplay();
|
|
2275
2368
|
}
|
|
2276
2369
|
|
|
2370
|
+
async function loadDatasets() {
|
|
2371
|
+
const region = document.getElementById('regionInput').value;
|
|
2372
|
+
const delay = document.getElementById('delayInput').value;
|
|
2373
|
+
const universe = document.getElementById('universeInput').value;
|
|
2374
|
+
const select = document.getElementById('datasetSelect');
|
|
2375
|
+
const btn = document.getElementById('loadDatasetsBtn');
|
|
2376
|
+
|
|
2377
|
+
try {
|
|
2378
|
+
const originalText = btn.textContent;
|
|
2379
|
+
btn.disabled = true;
|
|
2380
|
+
btn.textContent = 'Loading...';
|
|
2381
|
+
|
|
2382
|
+
if (!window.brainAPI || !window.brainAPI.isConnectedToBrain()) {
|
|
2383
|
+
throw new Error('Not connected to BRAIN');
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
const datasets = await window.brainAPI.getDatasets(region, parseInt(delay), universe);
|
|
2387
|
+
|
|
2388
|
+
// Clear and populate select
|
|
2389
|
+
select.innerHTML = '<option value="">Select dataset...</option>';
|
|
2390
|
+
|
|
2391
|
+
// Sort datasets by ID
|
|
2392
|
+
datasets.sort((a, b) => a.id.localeCompare(b.id));
|
|
2393
|
+
|
|
2394
|
+
datasets.forEach(ds => {
|
|
2395
|
+
const option = document.createElement('option');
|
|
2396
|
+
option.value = ds.id;
|
|
2397
|
+
option.textContent = `${ds.id} - ${ds.description || ds.name || ''}`;
|
|
2398
|
+
option.title = ds.description || ds.name || '';
|
|
2399
|
+
select.appendChild(option);
|
|
2400
|
+
});
|
|
2401
|
+
|
|
2402
|
+
select.style.display = 'block';
|
|
2403
|
+
btn.textContent = 'Reload List';
|
|
2404
|
+
|
|
2405
|
+
} catch (error) {
|
|
2406
|
+
alert(`Failed to load datasets: ${error.message}`);
|
|
2407
|
+
btn.textContent = 'Load List';
|
|
2408
|
+
} finally {
|
|
2409
|
+
btn.disabled = false;
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2277
2413
|
async function loadDataFields() {
|
|
2278
2414
|
const region = document.getElementById('regionInput').value;
|
|
2279
2415
|
const delay = document.getElementById('delayInput').value;
|
|
@@ -2566,6 +2702,10 @@ function setupDataFieldsModalEventListeners() {
|
|
|
2566
2702
|
|
|
2567
2703
|
loadBtn.onclick = loadDataFields;
|
|
2568
2704
|
|
|
2705
|
+
// Input listeners for cascading options
|
|
2706
|
+
document.getElementById('regionInput').addEventListener('input', updateDelayOptions);
|
|
2707
|
+
document.getElementById('delayInput').addEventListener('input', updateUniverseOptions);
|
|
2708
|
+
|
|
2569
2709
|
// Filter checkbox listeners
|
|
2570
2710
|
highCoverageFilter.onchange = () => populateDataFieldsList();
|
|
2571
2711
|
popularFilter.onchange = () => populateDataFieldsList();
|
|
@@ -330,19 +330,40 @@
|
|
|
330
330
|
<div class="params-row">
|
|
331
331
|
<div class="param-group">
|
|
332
332
|
<label for="regionInput">Region:</label>
|
|
333
|
-
<
|
|
333
|
+
<div style="display: flex; gap: 5px;">
|
|
334
|
+
<input type="text" id="regionInput" class="form-input" placeholder="e.g., USA" value="USA" style="flex: 1;">
|
|
335
|
+
<select id="regionSelect" class="form-input" style="flex: 1; display: none;" onchange="if(this.value) { document.getElementById('regionInput').value = this.value; updateDelayOptions(); }">
|
|
336
|
+
<option value="">Select...</option>
|
|
337
|
+
</select>
|
|
338
|
+
</div>
|
|
334
339
|
</div>
|
|
335
340
|
<div class="param-group">
|
|
336
341
|
<label for="delayInput">Delay:</label>
|
|
337
|
-
<
|
|
342
|
+
<div style="display: flex; gap: 5px;">
|
|
343
|
+
<input type="text" id="delayInput" class="form-input" placeholder="e.g., 1" value="1" style="flex: 1;">
|
|
344
|
+
<select id="delaySelect" class="form-input" style="flex: 1; display: none;" onchange="if(this.value) { document.getElementById('delayInput').value = this.value; updateUniverseOptions(); }">
|
|
345
|
+
<option value="">Select...</option>
|
|
346
|
+
</select>
|
|
347
|
+
</div>
|
|
338
348
|
</div>
|
|
339
349
|
<div class="param-group">
|
|
340
350
|
<label for="universeInput">Universe:</label>
|
|
341
|
-
<
|
|
351
|
+
<div style="display: flex; gap: 5px;">
|
|
352
|
+
<input type="text" id="universeInput" class="form-input" placeholder="e.g., TOP3000" value="TOP3000" style="flex: 1;">
|
|
353
|
+
<select id="universeSelect" class="form-input" style="flex: 1; display: none;" onchange="if(this.value) document.getElementById('universeInput').value = this.value">
|
|
354
|
+
<option value="">Select...</option>
|
|
355
|
+
</select>
|
|
356
|
+
</div>
|
|
342
357
|
</div>
|
|
343
358
|
<div class="param-group">
|
|
344
359
|
<label for="datasetInput">Dataset ID:</label>
|
|
345
|
-
<
|
|
360
|
+
<div style="display: flex; gap: 5px;">
|
|
361
|
+
<input type="text" id="datasetInput" class="form-input" placeholder="e.g., fundamental6" value="fundamental6" style="flex: 1;">
|
|
362
|
+
<select id="datasetSelect" class="form-input" style="flex: 1; display: none;" onchange="if(this.value) document.getElementById('datasetInput').value = this.value">
|
|
363
|
+
<option value="">Select dataset...</option>
|
|
364
|
+
</select>
|
|
365
|
+
<button id="loadDatasetsBtn" class="btn btn-small btn-secondary" onclick="loadDatasets()" title="Load Datasets from BRAIN">Load List</button>
|
|
366
|
+
</div>
|
|
346
367
|
</div>
|
|
347
368
|
<div class="param-group">
|
|
348
369
|
<button id="loadDataFieldsBtn" class="btn btn-secondary">Load Data Fields</button>
|