aayushkrm commited on
Commit
e6dcbc7
·
1 Parent(s): 2b559f3

Update UI with Technical Report and Metrics

Browse files
Files changed (1) hide show
  1. app.py +188 -57
app.py CHANGED
@@ -1,3 +1,7 @@
 
 
 
 
1
  import gradio as gr
2
  import torch
3
  import numpy as np
@@ -7,37 +11,48 @@ import os
7
  import sys
8
  from datasets import load_dataset
9
  from src.models import get_model
10
- from src.engine import quantize_model
11
 
12
- # --- CONFIGURATION ---
13
- DATASET_NAME = "aayushkrm/wunder-fund-hft-data"
 
 
 
 
 
 
14
 
15
- # --- LOAD DATASET ---
16
- print("Initializing App...")
17
  try:
18
- print("Loading FULL dataset from Hugging Face...")
19
- # FIX: Load "train" split entirely (141MB fits easily in memory)
20
- # This ensures we get ALL sequence IDs, not just the first one.
21
- dataset = load_dataset(DATASET_NAME, split="train")
22
- df = dataset.to_pandas()
23
-
24
- # --- TYPE CONVERSION ---
25
- df['seq_ix'] = df['seq_ix'].astype(int)
26
-
27
- # Get all unique IDs and sort them
28
- unique_ids_np = df['seq_ix'].unique()
29
- SEQ_IDS = sorted([int(x) for x in unique_ids_np.tolist()])
30
-
31
- print(f"✅ Loaded {len(df)} rows.")
32
- print(f"✅ Found {len(SEQ_IDS)} unique sequences: {SEQ_IDS[:5]}...")
33
-
34
  except Exception as e:
35
  print(f"⚠️ Could not load HF dataset: {e}")
36
  df = None
37
- SEQ_IDS = [0]
 
 
 
 
 
 
 
 
 
38
 
39
- # --- CACHED MODEL LOADER ---
40
  def load_cached_model():
 
41
  model = get_model("winner", input_size=32, hidden_size=256, layers=6)
42
  model_path = "artifacts/best_model.pt"
43
 
@@ -45,49 +60,60 @@ def load_cached_model():
45
  try:
46
  print(f"Loading weights from {model_path}...")
47
  state = torch.load(model_path, map_location='cpu')
 
48
  state = {k: v.float() for k, v in state.items()}
49
  model.load_state_dict(state)
50
- print("✅ Loaded best_model.pt")
 
51
  except Exception as e:
52
  print(f"⚠️ Error loading model: {e}")
53
  else:
54
  print("⚠️ Model file not found, using random weights.")
55
-
56
  model = quantize_model(model)
57
  return model
58
-
59
  MODEL = load_cached_model()
60
 
61
- def inference(seq_id_input, steps_input):
62
- seq_id = int(seq_id_input)
63
- steps_to_plot = int(steps_input)
64
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  if df is not None:
66
  seq_data = df[df['seq_ix'] == seq_id].sort_values('step_in_seq')
67
- if len(seq_data) == 0:
68
- raw_values = np.random.randn(1000, 32).astype(np.float32)
69
- else:
70
- raw_values = seq_data[[str(i) for i in range(32)]].values.astype(np.float32)
71
-
72
  mean = raw_values.mean(axis=0)
73
  std = raw_values.std(axis=0) + 1e-6
74
  norm_values = (raw_values - mean) / std
75
  else:
76
  norm_values = np.random.randn(1000, 32).astype(np.float32)
77
-
78
  x = torch.tensor(norm_values).unsqueeze(0)
79
-
80
  with torch.no_grad():
81
  preds = []
82
  h = None
83
  for t in range(min(len(x[0]), steps_to_plot)):
84
  xt = x[:, t:t+1, :]
85
  o, h = MODEL(xt, h)
86
- preds.append(float(o.numpy()[0,0,0]))
87
-
88
  fig = go.Figure()
89
  y_actual = [float(v) for v in norm_values[:steps_to_plot, 0].flatten()]
90
- y_pred = preds
91
  x_axis = list(range(len(y_actual)))
92
 
93
  fig.add_trace(go.Scatter(x=x_axis, y=y_actual, mode='lines', name='Actual', line=dict(color='gray')))
@@ -102,24 +128,129 @@ def inference(seq_id_input, steps_input):
102
  )
103
  return fig
104
 
105
- # --- UI ---
106
- with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
107
- gr.Markdown("# ⚡ Quant-Lab: HFT Sequence Modeling")
108
- gr.Markdown(f"**Strategy:** SE-Mish-DeepResGRU (Rank 28 Implementation)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
- with gr.Row():
111
- seq_selector = gr.Dropdown(
112
- choices=SEQ_IDS,
113
- label="Select Sequence ID",
114
- value=SEQ_IDS[0]
115
- )
116
- step_slider = gr.Slider(
117
- minimum=50, maximum=1000, value=200, label="Steps"
118
- )
 
 
 
 
119
 
120
- plot = gr.Plot(label="Forecast")
121
- btn = gr.Button("Run Inference", variant="primary")
122
- btn.click(inference, inputs=[seq_selector, step_slider], outputs=plot)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
  if __name__ == "__main__":
125
- demo.launch()
 
1
+ """
2
+ Gradio app for HFT Sequence Modeling.
3
+ Includes overview, ablation summary, code display.
4
+ """
5
  import gradio as gr
6
  import torch
7
  import numpy as np
 
11
  import sys
12
  from datasets import load_dataset
13
  from src.models import get_model
14
+ from src.engine import quantize_model, get_model_size
15
 
16
+ # ============== CONFIGURATION ==============
17
+ DATASET_NAME = "aayushkrm/wunder-fund-hft-data"
18
+ SAMPLE_DATA_LENGTH = 200 # Sample sequence length
19
+ LOAD_SAMPLE = True # Fast prototype by loading only first 1% of the data, else LOAD = False
20
+ BEST_MODEL_STRATEGY = "Strategy ED"
21
+ BEST_MODEL_DESC = "1. SE-Mish-DeepResGRU"
22
+ # ============== DATA LOADING ==============
23
+ print("Initializing...")
24
 
 
 
25
  try:
26
+ print("Loading dataset from Hugging Face...")
27
+ # Load first 1% just for demo speed (Adjusted for the same error)
28
+ if LOAD_SAMPLE:
29
+ print("Loading sample dataset")
30
+ df = load_dataset(DATASET_NAME, split="train[:1%]").to_pandas()
31
+ df['seq_ix'] = df['seq_ix'].astype(int)
32
+ SEQ_IDS = sorted([int(x) for x in df['seq_ix'].unique().tolist()])
33
+ else:
34
+ print("Loading Full dataset")
35
+ df = load_dataset(DATASET_NAME, split="train").to_pandas()
36
+ df['seq_ix'] = df['seq_ix'].astype(int)
37
+ SEQ_IDS = sorted([int(x) for x in df['seq_ix'].unique().tolist()])
38
+
 
 
 
39
  except Exception as e:
40
  print(f"⚠️ Could not load HF dataset: {e}")
41
  df = None
42
+ SEQ_IDS = [0, 1, 2] # DUMMY
43
+
44
+ # Load default dataframe for visualization
45
+ df_display = pd.DataFrame({
46
+ 'Time Step': list(range(100)),
47
+ 'Actual Feature 0': np.random.randn(100),
48
+ 'Predicted Feature 0': np.random.randn(100)
49
+ })
50
+
51
+ print("Building UI...")
52
 
53
+ # ============== LOAD MODEL ==============
54
  def load_cached_model():
55
+ # Load saved scaler
56
  model = get_model("winner", input_size=32, hidden_size=256, layers=6)
57
  model_path = "artifacts/best_model.pt"
58
 
 
60
  try:
61
  print(f"Loading weights from {model_path}...")
62
  state = torch.load(model_path, map_location='cpu')
63
+ # Verify dictionary load and convert to float32
64
  state = {k: v.float() for k, v in state.items()}
65
  model.load_state_dict(state)
66
+ print(f"✅ Loaded Rank 28 solution with structure. Key file:")
67
+ # Force correct behavior to load back quantized models
68
  except Exception as e:
69
  print(f"⚠️ Error loading model: {e}")
70
  else:
71
  print("⚠️ Model file not found, using random weights.")
 
72
  model = quantize_model(model)
73
  return model
 
74
  MODEL = load_cached_model()
75
 
76
+ # --- Data for Overview/Ablation study section ----
77
+ ablation_data = """
78
+ | Rank | Approach | Score |
79
+ |---|---|---|
80
+ | 28 | Strategy ED SE-Mish-Swarm | 0.3873 |
81
+ | N/A | Strategy DS Mish-GRU Massive | 0.3871 |
82
+ """
83
+
84
+ # --- Load data to create and download a table of models and score ---
85
+ def to_csv(data):
86
+ return data.to_csv(index=False)
87
+
88
+ df_log = pd.DataFrame([
89
+ {"Architecture":"SE-Mish-GRU", "Technique":"INT8","Size (MB)":9.9, "Score": 0.3873 },
90
+ {"Architecture":"WaveNet", "Technique":"TimeOut", "Size (MB)": 23.0 , "Score": 0.0},
91
+ ])
92
+
93
+ csv_text = to_csv(df_log)
94
+
95
+ def plot_forecast(seq_id, steps_to_plot):
96
  if df is not None:
97
  seq_data = df[df['seq_ix'] == seq_id].sort_values('step_in_seq')
98
+ raw_values = seq_data[[str(i) for i in range(32)]].values.astype(np.float32)
 
 
 
 
99
  mean = raw_values.mean(axis=0)
100
  std = raw_values.std(axis=0) + 1e-6
101
  norm_values = (raw_values - mean) / std
102
  else:
103
  norm_values = np.random.randn(1000, 32).astype(np.float32)
104
+
105
  x = torch.tensor(norm_values).unsqueeze(0)
 
106
  with torch.no_grad():
107
  preds = []
108
  h = None
109
  for t in range(min(len(x[0]), steps_to_plot)):
110
  xt = x[:, t:t+1, :]
111
  o, h = MODEL(xt, h)
112
+ preds.append(o.numpy()[0,0,0])
113
+
114
  fig = go.Figure()
115
  y_actual = [float(v) for v in norm_values[:steps_to_plot, 0].flatten()]
116
+ y_pred = [float(v) for v in preds]
117
  x_axis = list(range(len(y_actual)))
118
 
119
  fig.add_trace(go.Scatter(x=x_axis, y=y_actual, mode='lines', name='Actual', line=dict(color='gray')))
 
128
  )
129
  return fig
130
 
131
+ def get_code():
132
+ return inspect.getsource(PredictionModel.predict)
133
+
134
+ def json_code(spec):
135
+ if spec is None: return 0
136
+ try: return json.dumps(eval(spec))
137
+ except: return json_code(None)
138
+
139
+ def main():
140
+ # --- UI ---
141
+ # Add a Theme to the app
142
+ with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
143
+ with gr.Tab("Overview"):
144
+ gr.Markdown(
145
+ """
146
+ ## High-Frequency Trading Sequence Modeling
147
+ This project tackles the problem of predicting future market states from sequences of past states.
148
+ * **Dataset:** Anonymized features resembling real-world market data.
149
+ * **Objective:** Predict the next market state (32 features) given a history of 1000 steps.
150
+ * **Constraint:** Solutions must be deployable within a 20MB storage limit and run efficiently on CPU hardware.
151
+ """
152
+ )
153
+ gr.Markdown("### Key Results (Private Leaderboard)")
154
+ gr.Markdown(ablation_data)
155
+
156
+ # Data for Downloading Artifacts log as CSV
157
+ gr.File(value=csv_text, label="Download the Models Benchmark as CSV", file_name="models_report.csv")
158
 
159
+ with gr.Tab("Inference"):
160
+ gr.Markdown(f"### Model: {BEST_MODEL_STRATEGY}")
161
+ gr.Markdown(f"Description: {BEST_MODEL_DESC}")
162
+
163
+ with gr.Row():
164
+ seq_selector = gr.Dropdown(
165
+ choices=[int(x) for x in SEQ_IDS],
166
+ label="Select Market Sequence",
167
+ value=int(SEQ_IDS[0])
168
+ )
169
+ step_slider = gr.Slider(
170
+ minimum=50, maximum=1000, value=SAMPLE_DATA_LENGTH, label="Steps"
171
+ )
172
 
173
+ plot = gr.Plot(label="Forecast")
174
+ btn = gr.Button("Run Inference", variant="primary")
175
+ btn.click(plot_forecast, inputs=[seq_selector, step_slider], outputs=plot)
176
+
177
+ with gr.Tab("Code"):
178
+ gr.Code(value="""
179
+ import numpy as np
180
+ from pathlib import Path
181
+ import torch
182
+ import json
183
+ class PredictionModel:
184
+ def __init__(self):
185
+ torch.set_num_threads(1)
186
+ base = Path(__file__).parent
187
+ with open(base / "artifacts/config.json", "r") as f:
188
+ config = json.load(f)
189
+
190
+ scaler = np.load(base / "artifacts/scaler.npz")
191
+ self.mean = scaler["mean"].astype(np.float32)
192
+ self.std = scaler["std"].astype(np.float32)
193
+ self.std[self.std < 1e-6] = 1e-6
194
+
195
+ # Same R²-based weighting as Solution 6
196
+ self.weights = np.array(config['weights'], dtype=np.float32)
197
+
198
+ self.device = torch.device("cpu")
199
+ self.models = []
200
+
201
+ for i in range(config['num_models']):
202
+ model = Solution6GRU(config['input_size'], config['hidden_size'],
203
+ config['num_layers'], config['dropout'])
204
+ state_dict = torch.load(artifacts_dir / f"model_{i}.pt", map_location=self.device)
205
+ model.load_state_dict(state_dict)
206
+ model.to(self.device)
207
+ model.eval()
208
+ self.models.append(model)
209
+
210
+ self.current_seq_ix: Optional[int] = None
211
+ self.hidden_states: list = []
212
+
213
+ def _reset_state(self) -> None:
214
+ self.hidden_states = [None] * len(self.models)
215
+
216
+ def predict(self, data_point) -> np.ndarray | None:
217
+ if self.current_seq_ix != data_point.seq_ix:
218
+ self.current_seq_ix = data_point.seq_ix
219
+ self._reset_state()
220
+
221
+ state_arr = np.asarray(data_point.state, dtype=np.float32)
222
+ scaled = (state_arr - self.mean) / self.std
223
+ input_tensor = torch.from_numpy(scaled).view(1, 1, -1).to(self.device)
224
+
225
+ preds = []
226
+ with torch.no_grad():
227
+ for i, model in enumerate(self.models):
228
+ output, new_hidden = model(input_tensor, self.hidden_states[i])
229
+ self.hidden_states[i] = new_hidden.detach()
230
+ preds.append(output[0, 0, :].cpu().numpy())
231
+
232
+ if not data_point.need_prediction:
233
+ return None
234
+
235
+ # WEIGHTED average using R²-based weights (Solution 6 method)
236
+ preds_array = np.array(preds)
237
+ prediction = np.sum(preds_array * self.weights[:, np.newaxis], axis=0)
238
+ prediction = prediction * self.std + self.mean
239
+ return prediction.astype(np.float32)
240
+ """, language="python")
241
+
242
+
243
+ with gr.Accordion("Notes"):
244
+ gr.Markdown(
245
+ """
246
+ ## Further Improvements
247
+ 1. More sophisticated ensembling techniques.
248
+ 2. Dynamic code generation for hardware optimization.
249
+ """
250
+ )
251
+
252
+ with gr.Footer():
253
+ gr.Markdown("Created by Aayush Kumar. [GitHub Repository](https://github.com/aayushkrm/efficient-neural-hft)")
254
 
255
  if __name__ == "__main__":
256
+ demo.launch(debug=False)