topsecrettraders commited on
Commit
4a021d9
·
verified ·
1 Parent(s): eecb9f6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +26 -32
app.py CHANGED
@@ -108,7 +108,7 @@ def get_expiries(date: str, root: str):
108
  def get_instruments(date: str, root: str, expiry: str):
109
  root = root.upper()
110
  data = sb_rpc("get_instruments_for_expiry", {"p_date": date, "p_root": root, "p_expiry": expiry})
111
- instruments = [row['instrument_name'] for row in data]
112
 
113
  spot_fut =[]
114
  options =[]
@@ -121,7 +121,7 @@ def get_instruments(date: str, root: str, expiry: str):
121
  return JSONResponse({"spot_fut": sorted(spot_fut), "options": sorted(options)})
122
 
123
  # ==========================================
124
- # 4. API ROUTES - DATA FETCHING (EGRESS OPTIMIZED & FIXED)
125
  # ==========================================
126
 
127
  @app.post("/api/fetch_series")
@@ -135,7 +135,7 @@ async def fetch_series(
135
  end_time: str = Body(...),
136
  mode: str = Body("normal"),
137
  atm_range: int = Body(5),
138
- req_vars: List[str] = Body([]) # EGRESS REDUCTION: Only mapped vars returned!
139
  ):
140
  root = root.upper()
141
  t_start_dt = pd.to_datetime(f"{date} {start_time}:00")
@@ -189,7 +189,7 @@ async def fetch_series(
189
  if not all_records: return JSONResponse({"error": "No valid data found in range.", "labels":[]})
190
 
191
  df = pd.DataFrame(all_records)
192
- df.rename(columns={"tick_ts": "ts", "b_qty": "buy_qty", "s_qty": "sell_qty", "vol": "volume"}, inplace=True)
193
 
194
  df['ts'] = pd.to_datetime(df['ts'])
195
  df.drop_duplicates(subset=['ts'], inplace=True)
@@ -199,19 +199,19 @@ async def fetch_series(
199
  tf_map = {"1min": "1min", "3min": "3min", "5min": "5min", "15min": "15min", "30min": "30min", "1hour": "1h"}
200
  panda_tf = tf_map.get(timeframe, "1min")
201
 
202
- # FIX: Fyers is completely Cumulative! Taking "last" resolves the chart corruption directly.
203
- # Price is standard point-in-time, "last" applies correctly for the timeframe candle close.
204
- agg_dict = { col: "last" for col in["price", "volume", "oi", "buy_qty", "sell_qty", "cb", "cs", "pb", "ps"] if col in df.columns }
205
 
206
  resampled = df.resample(panda_tf).agg(agg_dict).ffill().fillna(0)
207
 
208
- # EGRESS OPTIMIZATION: Process Only Required Arrays dynamically parsed from formula!
209
  response_data = {"labels": resampled.index.strftime('%H:%M').tolist(), "P": resampled['price'].tolist() if 'price' in resampled else[]}
210
 
 
 
211
  for req in req_vars:
212
- col_map = {"V": "volume", "B": "buy_qty", "S": "sell_qty", "OI": "oi"}
213
- col = col_map.get(req.upper(), req.lower())
214
- if col in resampled.columns: response_data[req.upper()] = resampled[col].tolist()
215
 
216
  return JSONResponse(response_data)
217
 
@@ -524,22 +524,17 @@ const CustomDD = {
524
  const App = {
525
  state: { date: null, mode: 'LIVE', columns: 3, layout: [[],[],[]], charts: {}, refreshRate: 15 },
526
  presets: {
527
- "Model 1":[
528
  { formula: 'P', color1: '#2f81f7', cond: 'none', label: 'Price', axis: 'left' },
529
- { formula: 'B', color1: '#238636', cond: 'none', label: 'Buy', axis: 'right' },
530
- { formula: 'S', color1: '#da3633', cond: 'none', label: 'Sell', axis: 'right' }
531
- ],
532
- "Advanced Option Model":[
533
- { formula: 'P', color1: '#2f81f7', cond: 'none', label: 'Spot Price', axis: 'left' },
534
- { formula: 'CB + PS', color1: '#238636', cond: 'none', label: 'Net Buy Flow', axis: 'right' },
535
- { formula: 'CS + PB', color1: '#da3633', cond: 'none', label: 'Net Sell Flow', axis: 'right' }
536
  ]
537
  },
538
  async init() {
539
  CustomDD.init(); await this.loadDates();
540
- const storedPresets = localStorage.getItem('dc_pro_v4_presets');
541
  if(storedPresets) try { this.presets = { ...this.presets, ...JSON.parse(storedPresets) }; } catch(e){}
542
- const saved = localStorage.getItem('dc_pro_v4_final');
543
  if (saved) {
544
  try { this.state = { ...this.state, ...JSON.parse(saved) }; if(this.state.layout.length !== this.state.columns) this.setLayout(this.state.columns); }
545
  catch(e) { this.initDefault(); }
@@ -570,9 +565,9 @@ const App = {
570
  },
571
  save() {
572
  const cleanCharts = {}; for(let id in this.state.charts) cleanCharts[id] = { config: this.state.charts[id].config };
573
- localStorage.setItem('dc_pro_v4_final', JSON.stringify({ ...this.state, charts: cleanCharts }));
574
  },
575
- reset() { if(confirm("Reset Workspace?")) { localStorage.removeItem('dc_pro_v4_final'); location.reload(); } },
576
  setLayout(n) {
577
  const all = this.state.layout.flat(); this.state.columns = n; this.state.layout = Array.from({length: n}, () =>[]);
578
  all.forEach((id, i) => this.state.layout[i % n].push(id));
@@ -640,7 +635,7 @@ const App = {
640
 
641
  const ChartLogic = {
642
  extractVars(formula) {
643
- const validVars =['CB','CS','PB','PS','P','V','OI','B','S'];
644
  let requested =[];
645
  validVars.forEach(v => { if (new RegExp('\\b' + v + '\\b').test(formula)) requested.push(v); });
646
  return requested;
@@ -802,25 +797,24 @@ const Config = {
802
  document.getElementById('lblInst').innerText = mode === 'normal' ? 'Instrument' : 'Reference Spot/Fut (For ATM Calc)';
803
  document.getElementById('advInstHint').style.display = mode === 'advanced' ? 'block' : 'none';
804
  this.updateInfoTooltip(mode); this.updatePresetsDropdown();
805
- this.loadPreset(mode === 'advanced' ? "Advanced Option Model" : "Model 1");
806
  },
807
 
808
  updateInfoTooltip(mode) {
809
  const icon = document.getElementById('infoIcon'); if (!icon) return;
810
- if (mode === 'advanced') { icon.title = "Standard: P, V, OI, B, S \\nAdvanced Options ONLY: CB, CS, PB, PS\\nSupports Natural Formulas: e.g. CB + PS"; icon.style.color = "var(--accent)"; icon.style.borderColor = "var(--accent)"; }
811
- else { icon.title = "Standard: P, V, OI, B, S \\nSpecific Option Selected: CB/CS or PB/PS are automatically mapped from B/S."; icon.style.color = "var(--text-secondary)"; icon.style.borderColor = "var(--border)"; }
812
  },
813
 
814
  updatePresetsDropdown() { document.getElementById('cfgPresetsDropdown').innerHTML = Object.keys(App.presets).map(k => `<option value="${k}">${k}</option>`).join(''); },
815
  loadPreset(presetName = null) { const name = presetName || document.getElementById('cfgPresetsDropdown').value; if(name && App.presets[name]) { document.getElementById('cfgPresetsDropdown').value = name; this.renderSeries(App.presets[name]); } },
816
  savePreset() {
817
  const name = prompt("Enter a name for this Quick Configuration:", "Custom Model"); if(!name) return;
818
- App.presets[name] = this.extractSeriesFromDOM(); localStorage.setItem('dc_pro_v4_presets', JSON.stringify(App.presets));
819
  this.updatePresetsDropdown(); document.getElementById('cfgPresetsDropdown').value = name;
820
  },
821
  deletePreset() {
822
- const name = document.getElementById('cfgPresetsDropdown').value; if(name.startsWith('Model 1') || name.startsWith('Advanced Option')) return alert("Cannot delete default models.");
823
- if(confirm(`Delete model '${name}'?`)) { delete App.presets[name]; localStorage.setItem('dc_pro_v4_presets', JSON.stringify(App.presets)); this.updatePresetsDropdown(); this.loadPreset(); }
824
  },
825
 
826
  async open(id) {
@@ -843,7 +837,7 @@ const Config = {
843
  document.getElementById('advInstHint').style.display = this.currentMode === 'advanced' ? 'block' : 'none';
844
 
845
  this.updateInfoTooltip(this.currentMode);
846
- if (cfg.series && cfg.series.length > 0) this.renderSeries(cfg.series); else this.loadPreset(this.currentMode === 'advanced' ? "Advanced Option Model" : "Model 1");
847
 
848
  document.getElementById('cfgRoot').value = cfg.root || ''; document.getElementById('cfgExp').value = cfg.expiry || ''; document.getElementById('cfgInst').value = cfg.instrument || '';
849
  if(cfg.root) { await this.onRootChange(cfg.expiry, cfg.instrument); }
@@ -855,7 +849,7 @@ const Config = {
855
  document.getElementById('cfgLoading').style.display = 'none'; document.getElementById('cfgContent').style.display = 'block';
856
  document.getElementById('cfgGlobalOnly').style.display = 'block'; document.getElementById('cfgChartOnly').style.display = 'none';
857
  document.getElementById('cfgRefresh').value = App.state.refreshRate || 15;
858
- this.updatePresetsDropdown(); this.loadPreset("Model 1");
859
  },
860
  close() { document.getElementById('configModal').style.display = 'none'; },
861
 
 
108
  def get_instruments(date: str, root: str, expiry: str):
109
  root = root.upper()
110
  data = sb_rpc("get_instruments_for_expiry", {"p_date": date, "p_root": root, "p_expiry": expiry})
111
+ instruments =[row['instrument_name'] for row in data]
112
 
113
  spot_fut =[]
114
  options =[]
 
121
  return JSONResponse({"spot_fut": sorted(spot_fut), "options": sorted(options)})
122
 
123
  # ==========================================
124
+ # 4. API ROUTES - DATA FETCHING (EGRESS OPTIMIZED)
125
  # ==========================================
126
 
127
  @app.post("/api/fetch_series")
 
135
  end_time: str = Body(...),
136
  mode: str = Body("normal"),
137
  atm_range: int = Body(5),
138
+ req_vars: List[str] = Body([]) # EGRESS REDUCTION: Only requested logic passes through
139
  ):
140
  root = root.upper()
141
  t_start_dt = pd.to_datetime(f"{date} {start_time}:00")
 
189
  if not all_records: return JSONResponse({"error": "No valid data found in range.", "labels":[]})
190
 
191
  df = pd.DataFrame(all_records)
192
+ df.rename(columns={"tick_ts": "ts"}, inplace=True)
193
 
194
  df['ts'] = pd.to_datetime(df['ts'])
195
  df.drop_duplicates(subset=['ts'], inplace=True)
 
199
  tf_map = {"1min": "1min", "3min": "3min", "5min": "5min", "15min": "15min", "30min": "30min", "1hour": "1h"}
200
  panda_tf = tf_map.get(timeframe, "1min")
201
 
202
+ # Strictly take the LAST cumulative Fyers variable output for the candle
203
+ agg_dict = { col: "last" for col in["price", "vol", "oi", "cb", "cs", "pb", "ps"] if col in df.columns }
 
204
 
205
  resampled = df.resample(panda_tf).agg(agg_dict).ffill().fillna(0)
206
 
 
207
  response_data = {"labels": resampled.index.strftime('%H:%M').tolist(), "P": resampled['price'].tolist() if 'price' in resampled else[]}
208
 
209
+ # Maps what frontend asks for (e.g., 'V') to the database columns (e.g., 'vol')
210
+ col_map = {"V": "vol", "OI": "oi", "CB": "cb", "CS": "cs", "PB": "pb", "PS": "ps"}
211
  for req in req_vars:
212
+ col = col_map.get(req.upper())
213
+ if col and col in resampled.columns:
214
+ response_data[req.upper()] = resampled[col].tolist()
215
 
216
  return JSONResponse(response_data)
217
 
 
524
  const App = {
525
  state: { date: null, mode: 'LIVE', columns: 3, layout: [[],[],[]], charts: {}, refreshRate: 15 },
526
  presets: {
527
+ "Default Flow":[
528
  { formula: 'P', color1: '#2f81f7', cond: 'none', label: 'Price', axis: 'left' },
529
+ { formula: 'CB + PS', color1: '#238636', cond: 'none', label: 'Bullish Flow', axis: 'right' },
530
+ { formula: 'PB + CS', color1: '#da3633', cond: 'none', label: 'Bearish Flow', axis: 'right' }
 
 
 
 
 
531
  ]
532
  },
533
  async init() {
534
  CustomDD.init(); await this.loadDates();
535
+ const storedPresets = localStorage.getItem('dc_pro_v5_presets');
536
  if(storedPresets) try { this.presets = { ...this.presets, ...JSON.parse(storedPresets) }; } catch(e){}
537
+ const saved = localStorage.getItem('dc_pro_v5_final');
538
  if (saved) {
539
  try { this.state = { ...this.state, ...JSON.parse(saved) }; if(this.state.layout.length !== this.state.columns) this.setLayout(this.state.columns); }
540
  catch(e) { this.initDefault(); }
 
565
  },
566
  save() {
567
  const cleanCharts = {}; for(let id in this.state.charts) cleanCharts[id] = { config: this.state.charts[id].config };
568
+ localStorage.setItem('dc_pro_v5_final', JSON.stringify({ ...this.state, charts: cleanCharts }));
569
  },
570
+ reset() { if(confirm("Reset Workspace?")) { localStorage.removeItem('dc_pro_v5_final'); location.reload(); } },
571
  setLayout(n) {
572
  const all = this.state.layout.flat(); this.state.columns = n; this.state.layout = Array.from({length: n}, () =>[]);
573
  all.forEach((id, i) => this.state.layout[i % n].push(id));
 
635
 
636
  const ChartLogic = {
637
  extractVars(formula) {
638
+ const validVars =['CB','CS','PB','PS','P','V','OI'];
639
  let requested =[];
640
  validVars.forEach(v => { if (new RegExp('\\b' + v + '\\b').test(formula)) requested.push(v); });
641
  return requested;
 
797
  document.getElementById('lblInst').innerText = mode === 'normal' ? 'Instrument' : 'Reference Spot/Fut (For ATM Calc)';
798
  document.getElementById('advInstHint').style.display = mode === 'advanced' ? 'block' : 'none';
799
  this.updateInfoTooltip(mode); this.updatePresetsDropdown();
800
+ this.loadPreset("Default Flow");
801
  },
802
 
803
  updateInfoTooltip(mode) {
804
  const icon = document.getElementById('infoIcon'); if (!icon) return;
805
+ icon.title = "Standard Variables Available:\\nP, V, OI, CB, CS, PB, PS\\nSupports Natural Formulas: e.g. CB + PS";
 
806
  },
807
 
808
  updatePresetsDropdown() { document.getElementById('cfgPresetsDropdown').innerHTML = Object.keys(App.presets).map(k => `<option value="${k}">${k}</option>`).join(''); },
809
  loadPreset(presetName = null) { const name = presetName || document.getElementById('cfgPresetsDropdown').value; if(name && App.presets[name]) { document.getElementById('cfgPresetsDropdown').value = name; this.renderSeries(App.presets[name]); } },
810
  savePreset() {
811
  const name = prompt("Enter a name for this Quick Configuration:", "Custom Model"); if(!name) return;
812
+ App.presets[name] = this.extractSeriesFromDOM(); localStorage.setItem('dc_pro_v5_presets', JSON.stringify(App.presets));
813
  this.updatePresetsDropdown(); document.getElementById('cfgPresetsDropdown').value = name;
814
  },
815
  deletePreset() {
816
+ const name = document.getElementById('cfgPresetsDropdown').value; if(name.startsWith('Default Flow')) return alert("Cannot delete default models.");
817
+ if(confirm(`Delete model '${name}'?`)) { delete App.presets[name]; localStorage.setItem('dc_pro_v5_presets', JSON.stringify(App.presets)); this.updatePresetsDropdown(); this.loadPreset(); }
818
  },
819
 
820
  async open(id) {
 
837
  document.getElementById('advInstHint').style.display = this.currentMode === 'advanced' ? 'block' : 'none';
838
 
839
  this.updateInfoTooltip(this.currentMode);
840
+ if (cfg.series && cfg.series.length > 0) this.renderSeries(cfg.series); else this.loadPreset("Default Flow");
841
 
842
  document.getElementById('cfgRoot').value = cfg.root || ''; document.getElementById('cfgExp').value = cfg.expiry || ''; document.getElementById('cfgInst').value = cfg.instrument || '';
843
  if(cfg.root) { await this.onRootChange(cfg.expiry, cfg.instrument); }
 
849
  document.getElementById('cfgLoading').style.display = 'none'; document.getElementById('cfgContent').style.display = 'block';
850
  document.getElementById('cfgGlobalOnly').style.display = 'block'; document.getElementById('cfgChartOnly').style.display = 'none';
851
  document.getElementById('cfgRefresh').value = App.state.refreshRate || 15;
852
+ this.updatePresetsDropdown(); this.loadPreset("Default Flow");
853
  },
854
  close() { document.getElementById('configModal').style.display = 'none'; },
855