Oviya commited on
Commit
df4a76e
·
1 Parent(s): b5872a0

update toolspage

Browse files
public/globe.png ADDED

Git LFS Details

  • SHA256: b9be71d5c4ebb76789bf900ccac677011920506abe2fc07572e3af9261f10096
  • Pointer size: 132 Bytes
  • Size of remote file: 1.69 MB
src/app/toolspage/toolspage.component.html CHANGED
@@ -30,10 +30,14 @@
30
  </mat-select>
31
  </mat-form-field>
32
 
33
- <!-- Companies (multi-select) -->
34
  <mat-form-field appearance="fill" class="full">
35
  <mat-label>Select Companies (multi-select)</mat-label>
36
- <mat-select [(ngModel)]="selectedCompanies" name="companies" [disabled]="companies.length === 0" multiple>
 
 
 
 
37
  <mat-option (click)="toggleSelectAll()" [disabled]="companies.length === 0">
38
  {{ isAllSelected() ? 'Unselect All' : 'Select All' }}
39
  </mat-option>
@@ -42,25 +46,6 @@
42
  </mat-option>
43
  </mat-select>
44
  </mat-form-field>
45
-
46
- <!-- Trading Type -->
47
- <!--<mat-form-field appearance="fill" class="col">
48
- <mat-label>Trading Type</mat-label>
49
- <mat-select [(ngModel)]="selectedTradingType" name="tradingType"
50
- [disabled]="!selectedCompanies || selectedCompanies.length === 0">
51
- <mat-option value="intraday">Intraday</mat-option>
52
- <mat-option value="swing">Short-Term Swing Trading</mat-option>
53
- <mat-option value="longTerm">Long-Term Investment</mat-option>
54
- </mat-select>
55
- </mat-form-field>-->
56
- <!-- Run Analysis -->
57
- <!--<button mat-raised-button
58
- type="button"
59
- class="run-btn"
60
- (click)="submit()"
61
- [disabled]="loading || !selectedTradingType">
62
- Run Analysis
63
- </button>-->
64
  </form>
65
  </section>
66
 
@@ -68,21 +53,12 @@
68
  <h2 class="title">Search</h2>
69
 
70
  <form class="grid" autocomplete="off">
71
- <!-- Trading Type (independent) -->
72
- <!--<mat-form-field appearance="fill" class="col">
73
- <mat-label>Trading Type</mat-label>
74
- <mat-select [(ngModel)]="selectedTradingType" name="tradingType">
75
- <mat-option value="intraday">Intraday</mat-option>
76
- <mat-option value="swing">Short-Term Swing Trading</mat-option>
77
- <mat-option value="longTerm">Long-Term Investment</mat-option>
78
- </mat-select>
79
- </mat-form-field>-->
80
- <!-- Independent Ticker Search (uses static TICKERS) -->
81
  <mat-form-field appearance="fill" class="full ticker-search">
82
  <mat-label>Search ticker</mat-label>
83
  <input matInput
84
  type="text"
85
- placeholder="Type symbol or company name (e.g., INFY, RELIANCE)"
86
  [formControl]="tickerCtrl"
87
  [matAutocomplete]="tickAuto" />
88
  <button mat-icon-button matSuffix *ngIf="tickerCtrl.value" (click)="clearTickerSearch()" aria-label="Clear">
@@ -92,37 +68,36 @@
92
  </mat-form-field>
93
 
94
  <mat-autocomplete #tickAuto="matAutocomplete" (optionSelected)="onPickTicker($event.option.value)">
95
- <mat-option *ngFor="let c of filteredCompanies$ | async; trackBy: trackBySymbol" [value]="c">
96
- <strong>{{ c.symbol }}</strong>&nbsp;—&nbsp;{{ c.company }}
 
 
 
 
97
  </mat-option>
98
  </mat-autocomplete>
99
 
100
- <!-- Selected tickers (independent list) -->
101
- <mat-chip-listbox *ngIf="pickedTickers.length" class="chipbar" aria-label="Picked tickers">
102
- <mat-chip-row *ngFor="let t of pickedTickers; trackBy: trackBySymbol" [removable]="true" (removed)="removeTicker(t.symbol)">
103
- {{ t.symbol }} — {{ t.company }}
 
 
 
 
 
104
  <button matChipRemove aria-label="Remove"><mat-icon>close</mat-icon></button>
105
  </mat-chip-row>
106
  </mat-chip-listbox>
107
-
108
- <!-- Run Analysis (optional reuse) -->
109
- <!--<button mat-raised-button
110
- type="button"
111
- class="run-btn"
112
- (click)="submit()"
113
- [disabled]="loading || !selectedTradingType">
114
- Run Analysis
115
- </button>-->
116
  </form>
117
  </section>
118
  </div>
 
119
  <div class="type-content">
120
  <section class="filters-card">
121
  <h2 class="title">Trading Type</h2>
122
 
123
  <form class="grid" autocomplete="off">
124
-
125
- <!-- Trading Type -->
126
  <mat-form-field appearance="fill" class="col">
127
  <mat-label>Trading Type</mat-label>
128
  <mat-select [(ngModel)]="selectedTradingType" name="tradingType"
@@ -139,12 +114,9 @@
139
  <mat-option value="futures">Futures Trading</mat-option>
140
  <mat-option value="arbitrage">Arbitrage Trading</mat-option>
141
  <mat-option value="eventDriven">Event-Driven Trading</mat-option>
142
-
143
  </mat-select>
144
  </mat-form-field>
145
 
146
-
147
- <!-- Run Analysis -->
148
  <button mat-raised-button
149
  type="button"
150
  class="run-btn"
@@ -155,13 +127,8 @@
155
  </form>
156
  </section>
157
  </div>
158
- <!--<footer></footer>-->
159
  </div>
160
 
161
-
162
-
163
-
164
-
165
  <!-- Loading overlay -->
166
  <div class="overlay" *ngIf="loading">
167
  <mat-spinner diameter="100"></mat-spinner>
 
30
  </mat-select>
31
  </mat-form-field>
32
 
33
+ <!-- Companies (multi-select) bound to indexSelected -->
34
  <mat-form-field appearance="fill" class="full">
35
  <mat-label>Select Companies (multi-select)</mat-label>
36
+ <mat-select [(ngModel)]="indexSelected"
37
+ (ngModelChange)="onCompaniesModelChange($event)"
38
+ name="companies"
39
+ [disabled]="companies.length === 0"
40
+ multiple>
41
  <mat-option (click)="toggleSelectAll()" [disabled]="companies.length === 0">
42
  {{ isAllSelected() ? 'Unselect All' : 'Select All' }}
43
  </mat-option>
 
46
  </mat-option>
47
  </mat-select>
48
  </mat-form-field>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  </form>
50
  </section>
51
 
 
53
  <h2 class="title">Search</h2>
54
 
55
  <form class="grid" autocomplete="off">
56
+ <!-- Search ticker/company via backend (/searchcompanies) -->
 
 
 
 
 
 
 
 
 
57
  <mat-form-field appearance="fill" class="full ticker-search">
58
  <mat-label>Search ticker</mat-label>
59
  <input matInput
60
  type="text"
61
+ placeholder="Type symbol or company (e.g., INFY, RELIANCE)"
62
  [formControl]="tickerCtrl"
63
  [matAutocomplete]="tickAuto" />
64
  <button mat-icon-button matSuffix *ngIf="tickerCtrl.value" (click)="clearTickerSearch()" aria-label="Clear">
 
68
  </mat-form-field>
69
 
70
  <mat-autocomplete #tickAuto="matAutocomplete" (optionSelected)="onPickTicker($event.option.value)">
71
+ <mat-option *ngFor="let h of filteredCompanies$ | async; trackBy: trackBySymbol" [value]="h">
72
+ <div style="display:flex;align-items:center;gap:10px;width:100%">
73
+ <strong style="min-width:90px">{{ h.symbol }}</strong>
74
+ <span style="flex:1;opacity:.85;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{ h.company }}</span>
75
+ <span style="font-size:12px;opacity:.75">{{ h.indexCode }}</span>
76
+ </div>
77
  </mat-option>
78
  </mat-autocomplete>
79
 
80
+ <!-- Chips: ONLY search-picked items -->
81
+ <mat-chip-listbox *ngIf="selectedChips.length" class="chipbar" aria-label="Picked tickers">
82
+ <mat-chip-row *ngFor="let t of selectedChips; trackBy: trackBySymbol"
83
+ [removable]="true"
84
+ (removed)="onRemoveChip(t.symbol)">
85
+ {{ t.symbol }}
86
+ <ng-container *ngIf="t.company && t.company !== t.symbol">
87
+ — {{ t.company }}
88
+ </ng-container>
89
  <button matChipRemove aria-label="Remove"><mat-icon>close</mat-icon></button>
90
  </mat-chip-row>
91
  </mat-chip-listbox>
 
 
 
 
 
 
 
 
 
92
  </form>
93
  </section>
94
  </div>
95
+
96
  <div class="type-content">
97
  <section class="filters-card">
98
  <h2 class="title">Trading Type</h2>
99
 
100
  <form class="grid" autocomplete="off">
 
 
101
  <mat-form-field appearance="fill" class="col">
102
  <mat-label>Trading Type</mat-label>
103
  <mat-select [(ngModel)]="selectedTradingType" name="tradingType"
 
114
  <mat-option value="futures">Futures Trading</mat-option>
115
  <mat-option value="arbitrage">Arbitrage Trading</mat-option>
116
  <mat-option value="eventDriven">Event-Driven Trading</mat-option>
 
117
  </mat-select>
118
  </mat-form-field>
119
 
 
 
120
  <button mat-raised-button
121
  type="button"
122
  class="run-btn"
 
127
  </form>
128
  </section>
129
  </div>
 
130
  </div>
131
 
 
 
 
 
132
  <!-- Loading overlay -->
133
  <div class="overlay" *ngIf="loading">
134
  <mat-spinner diameter="100"></mat-spinner>
src/app/toolspage/toolspage.component.scss CHANGED
@@ -7,8 +7,8 @@
7
  gap:2vw;
8
  place-items: center;
9
  color: #e6edf7;
10
- background: url('../../../public/filter-img.png') no-repeat;
11
- background-size: cover;
12
  }
13
 
14
  .filters-content {
@@ -21,12 +21,12 @@
21
  .filters-card {
22
  width: 30vw;
23
  padding: 1vw;
24
- /* margin-top: 4vw;*/
25
- /*background: rgba(14, 20, 36, 0.9);*/
26
- background-color: #000000;
27
- /*border: 1px solid #FF0000;*/
28
  border-radius: 1vw;
29
- box-shadow: 0 12px 36px rgba(0, 0, 0, 0.55);
 
 
 
30
  }
31
 
32
 
 
7
  gap:2vw;
8
  place-items: center;
9
  color: #e6edf7;
10
+ background: url('../../../public/globe.png') no-repeat;
11
+ /* background-size: cover;*/
12
  }
13
 
14
  .filters-content {
 
21
  .filters-card {
22
  width: 30vw;
23
  padding: 1vw;
24
+ background: rgba(10, 12, 16, 0.92);
 
 
 
25
  border-radius: 1vw;
26
+ box-shadow: 0 12px 12px -6px rgba(255,255,255,0.6), /* bottom */
27
+ 0 -12px 12px -6px rgba(255,255,255,0.6), /* top */
28
+ 12px 0 12px -6px rgba(255,255,255,0.6), /* right */
29
+ -12px 0 12px -6px rgba(255,255,255,0.6); /* left */
30
  }
31
 
32
 
src/app/toolspage/toolspage.component.ts CHANGED
@@ -11,17 +11,28 @@ import { MatIconModule } from '@angular/material/icon';
11
  import { MatButtonModule } from '@angular/material/button';
12
  import { MatChipsModule } from '@angular/material/chips';
13
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
14
-
15
  import { MarketService } from '../marketselect/market.service';
16
  import { Router } from '@angular/router';
17
- import { finalize, Observable } from 'rxjs';
18
- import { debounceTime, map, startWith } from 'rxjs/operators';
19
 
20
- interface CompanyOption { company: string; symbol: string; }
21
- interface IndexRef { code: string; name: string; }
 
 
22
  interface Company { symbol: string; company: string; }
 
 
23
  type MarketMap = Record<string, Record<string, IndexRef[]>>;
24
 
 
 
 
 
 
 
 
 
 
25
  @Component({
26
  selector: 'app-toolspage',
27
  standalone: true,
@@ -36,53 +47,29 @@ type MarketMap = Record<string, Record<string, IndexRef[]>>;
36
  MatIconModule,
37
  MatButtonModule,
38
  MatChipsModule,
39
- MatProgressSpinnerModule
 
 
40
  ],
41
  templateUrl: './toolspage.component.html',
42
  styleUrls: ['./toolspage.component.scss']
43
  })
44
  export class ToolspageComponent {
45
 
46
- // ---------- Search (independent)
47
- tickerCtrl = new FormControl<string>('', { nonNullable: true });
48
- filteredCompanies$!: Observable<CompanyOption[]>;
49
- pickedTickers: CompanyOption[] = [];
50
-
51
- // A small static set (edit/expand as you like)
52
- private readonly TICKERS: CompanyOption[] = [
53
- { symbol: 'RELIANCE', company: 'Reliance Industries' },
54
- { symbol: 'TCS', company: 'Tata Consultancy Services' },
55
- { symbol: 'INFY', company: 'Infosys' },
56
- { symbol: 'HDFCBANK', company: 'HDFC Bank' },
57
- { symbol: 'ICICIBANK', company: 'ICICI Bank' },
58
- { symbol: 'KOTAKBANK', company: 'Kotak Mahindra Bank' },
59
- { symbol: 'SBIN', company: 'State Bank of India' },
60
- { symbol: 'ITC', company: 'ITC' },
61
- { symbol: 'LT', company: 'Larsen & Toubro' },
62
- { symbol: 'HINDUNILVR', company: 'Hindustan Unilever' },
63
- { symbol: 'AXISBANK', company: 'Axis Bank' },
64
- { symbol: 'BAJFINANCE', company: 'Bajaj Finance' },
65
- { symbol: 'BHARTIARTL', company: 'Bharti Airtel' },
66
- { symbol: 'ASIANPAINT', company: 'Asian Paints' },
67
- { symbol: 'TITAN', company: 'Titan Company' },
68
- { symbol: 'ULTRACEMCO', company: 'UltraTech Cement' },
69
- { symbol: 'WIPRO', company: 'Wipro' },
70
- { symbol: 'HCLTECH', company: 'HCL Technologies' },
71
- { symbol: 'MARUTI', company: 'Maruti Suzuki' },
72
- { symbol: 'TATAMOTORS', company: 'Tata Motors' },
73
- { symbol: 'TATASTEEL', company: 'Tata Steel' },
74
- { symbol: 'M&M', company: 'Mahindra & Mahindra' }
75
- ];
76
-
77
- // ---------- Backend / config
78
- private readonly API =
79
- location.hostname.endsWith('hf.space')
80
- ? 'https://pykara-pytrade-backend.hf.space'
81
- : 'http://127.0.0.1:5000';
82
  loading = false;
83
  error: string | null = null;
84
 
85
- // ---------- Filters
 
 
 
 
 
86
  markets: MarketMap = {
87
  India: {
88
  'NSE (National Stock Exchange)': [
@@ -92,78 +79,123 @@ export class ToolspageComponent {
92
  { code: 'NIFTYMID100', name: 'NIFTY Midcap 100' },
93
  { code: 'NIFTY500', name: 'NIFTY 500' }
94
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  }
96
  };
97
 
98
- // ---------- Selections
99
  selectedCountry = '';
100
  selectedExchange = '';
101
  selectedIndexName = '';
 
 
102
  selectedCompanies: string[] = [];
103
- selectedTradingType = 'swing';
104
 
105
- // ---------- Derived lists
 
 
 
 
 
 
 
 
 
 
 
106
  countries = Object.keys(this.markets);
107
  exchanges: string[] = [];
108
  indices: IndexRef[] = [];
109
  companies: Company[] = [];
110
 
111
- // ---------- UI flags
 
 
 
 
112
  allSelected = false;
113
- loadingCompanies = false;
114
 
115
- constructor(private http: HttpClient, private marketService: MarketService, private router: Router) { }
 
 
 
 
 
116
 
 
117
  ngOnInit(): void {
 
118
  this.filteredCompanies$ = this.tickerCtrl.valueChanges.pipe(
119
  startWith(''),
120
- debounceTime(150),
121
- map(q => this.filterCompanies((q || '').trim().toLowerCase()))
 
 
 
 
 
 
 
 
 
 
 
122
  );
123
  }
124
 
125
- // -------- Search helpers
126
- private filterCompanies(q: string): CompanyOption[] {
127
- const list = this.TICKERS;
128
- if (!q) return list.slice(0, 15);
129
- return list.filter(c =>
130
- c.symbol.toLowerCase().includes(q) || c.company.toLowerCase().includes(q)
131
- ).slice(0, 20);
132
- }
133
 
134
- onPickTicker(c: CompanyOption): void {
135
- if (!this.pickedTickers.some(x => x.symbol === c.symbol)) {
136
- this.pickedTickers = [...this.pickedTickers, c];
137
  }
138
- this.tickerCtrl.setValue(''); // clear after selection
 
 
139
  }
140
 
141
- clearTickerSearch(): void { this.tickerCtrl.setValue(''); }
142
- removeTicker(symbol: string): void {
143
- this.pickedTickers = this.pickedTickers.filter(t => t.symbol !== symbol);
144
  }
145
 
146
- // -------- Market selection handlers
147
  onCountryChange(): void {
148
  this.error = null;
149
  this.selectedExchange = '';
150
  this.selectedIndexName = '';
151
- this.selectedCompanies = [];
152
- this.companies = [];
153
- this.allSelected = false;
154
-
155
  this.exchanges = this.selectedCountry ? Object.keys(this.markets[this.selectedCountry]) : [];
156
  this.indices = [];
 
 
 
157
  }
158
 
159
  onExchangeChange(): void {
160
  this.error = null;
161
  this.selectedIndexName = '';
162
- this.selectedCompanies = [];
163
- this.companies = [];
164
- this.allSelected = false;
165
-
166
  this.indices = this.markets[this.selectedCountry]?.[this.selectedExchange] ?? [];
 
 
 
167
  }
168
 
169
  onIndexChange(): void {
@@ -175,9 +207,8 @@ export class ToolspageComponent {
175
 
176
  const indexCode = (ref?.code || this.deriveIndexCode(this.selectedIndexName)).toUpperCase();
177
 
178
- this.selectedCompanies = [];
179
  this.companies = [];
180
- this.allSelected = false;
181
 
182
  if (!indexCode) {
183
  this.error = 'Unable to resolve index code from selection.';
@@ -186,16 +217,53 @@ export class ToolspageComponent {
186
  this.fetchCompanies(indexCode);
187
  }
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  toggleSelectAll(): void {
190
  this.allSelected = !this.allSelected;
191
- this.selectedCompanies = this.allSelected ? this.companies.map(c => c.symbol) : [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  }
 
193
  isAllSelected(): boolean {
194
- return this.companies.length > 0 && this.selectedCompanies.length === this.companies.length;
 
 
195
  }
196
- trackBySymbol(_: number, c: { symbol: string }) { return c.symbol; }
197
 
198
- // -------- Backend
 
 
 
 
199
  private fetchCompanies(indexCode: string): void {
200
  this.loadingCompanies = true;
201
  const params = new HttpParams().set('code', indexCode);
@@ -204,25 +272,42 @@ export class ToolspageComponent {
204
  `${this.API}/getcompanies`,
205
  { params }
206
  ).subscribe({
207
- next: res => {
208
  this.error = null;
209
  this.companies = res?.constituents ?? [];
210
- const allowed = new Set(this.companies.map(c => c.symbol));
211
- this.selectedCompanies = this.selectedCompanies.filter(s => allowed.has(s));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  this.allSelected = this.isAllSelected();
213
  this.loadingCompanies = false;
214
  },
215
- error: err => {
216
  console.error('GET /getcompanies failed', err);
217
  this.error = (err?.error?.error as string) || 'Failed to load companies.';
218
  this.companies = [];
219
- this.selectedCompanies = [];
 
 
220
  this.allSelected = false;
221
  this.loadingCompanies = false;
222
  }
223
  });
224
  }
225
 
 
226
  private deriveIndexCode(name: string): string {
227
  return (name || '')
228
  .toUpperCase()
@@ -232,64 +317,119 @@ export class ToolspageComponent {
232
  .replace(/[^A-Z0-9]/g, '');
233
  }
234
 
235
- getMarket(): string[] { return this.exchanges; }
236
- getMarketDivision(): string[] { return this.indices.map(i => i.name); }
237
- getCompanies(): Company[] { return this.companies; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
- // -------- Common controls
 
 
 
 
 
 
 
 
240
  canRunAnalysis(): boolean {
241
- const hasTickers = this.selectedCompanies.length > 0 || this.pickedTickers.length > 0;
242
- return !!this.selectedTradingType && hasTickers;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  }
244
 
245
  resetAll(): void {
246
  this.error = null;
247
 
248
- // Market Selection
249
  this.selectedCountry = '';
250
  this.selectedExchange = '';
251
  this.selectedIndexName = '';
252
- this.selectedCompanies = [];
253
  this.exchanges = [];
254
  this.indices = [];
255
  this.companies = [];
256
- this.allSelected = false;
 
 
 
 
 
 
257
 
258
  // Search
259
  this.tickerCtrl.setValue('');
260
- this.pickedTickers = [];
261
-
262
- // Common
263
- this.selectedTradingType = '';
264
  }
265
 
266
- // -------- Submit
267
- submit(): void {
268
- const symbols =
269
- this.selectedCompanies.length > 0
270
- ? this.selectedCompanies
271
- : this.pickedTickers.map(t => t.symbol);
272
 
273
- if (!this.selectedTradingType) {
274
- this.error = 'Please select a trading type.';
275
- return;
276
- }
277
- if (symbols.length === 0) {
278
- this.error = 'Please select at least one company or ticker.';
279
- return;
280
  }
 
281
 
282
- this.error = null;
283
- this.loading = true;
284
-
285
- this.marketService.getAnalyseResult(symbols)
286
- .pipe(finalize(() => (this.loading = false)))
287
- .subscribe({
288
- next: res => this.router.navigate(['/analysepage'], { state: { result: res } }),
289
- error: err => {
290
- console.error('API error:', err);
291
- this.error = 'Analysis failed. Please try again.';
292
- }
293
- });
294
  }
295
  }
 
11
  import { MatButtonModule } from '@angular/material/button';
12
  import { MatChipsModule } from '@angular/material/chips';
13
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
14
+ import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
15
  import { MarketService } from '../marketselect/market.service';
16
  import { Router } from '@angular/router';
 
 
17
 
18
+ import { Observable, of } from 'rxjs';
19
+ import { debounceTime, finalize, map, startWith, switchMap } from 'rxjs/operators';
20
+
21
+ // --- Types ---
22
  interface Company { symbol: string; company: string; }
23
+ interface CompanyOption { symbol: string; company: string; } // for chips view
24
+ interface IndexRef { code: string; name: string; }
25
  type MarketMap = Record<string, Record<string, IndexRef[]>>;
26
 
27
+ interface SearchHit {
28
+ symbol: string;
29
+ company: string;
30
+ indexCode: string;
31
+ indexName: string;
32
+ exchange: string;
33
+ country: string;
34
+ }
35
+
36
  @Component({
37
  selector: 'app-toolspage',
38
  standalone: true,
 
47
  MatIconModule,
48
  MatButtonModule,
49
  MatChipsModule,
50
+ MatProgressSpinnerModule,
51
+
52
+ MatSnackBarModule
53
  ],
54
  templateUrl: './toolspage.component.html',
55
  styleUrls: ['./toolspage.component.scss']
56
  })
57
  export class ToolspageComponent {
58
 
59
+ // ---------- Backend / config ----------
60
+ private readonly API =
61
+ location.hostname.endsWith('hf.space')
62
+ ? 'https://pykara-pytrade-backend.hf.space'
63
+ : 'http://127.0.0.1:5000';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  loading = false;
65
  error: string | null = null;
66
 
67
+ // ---------- Global search (backend-powered) ----------
68
+ tickerCtrl = new FormControl<string>('', { nonNullable: true });
69
+ filteredCompanies$!: Observable<SearchHit[]>;
70
+ globalLoading = false;
71
+
72
+ // ---------- Filters (country → exchange → index) ----------
73
  markets: MarketMap = {
74
  India: {
75
  'NSE (National Stock Exchange)': [
 
79
  { code: 'NIFTYMID100', name: 'NIFTY Midcap 100' },
80
  { code: 'NIFTY500', name: 'NIFTY 500' }
81
  ]
82
+ },
83
+ 'United States': {
84
+ NASDAQ: [
85
+ { code: 'NASDAQ100', name: 'NASDAQ-100' }
86
+ ]
87
+ },
88
+ Germany: {
89
+ 'XETRA (Deutsche Börse)': [
90
+ { code: 'DAX40', name: 'DAX 40' }
91
+ ]
92
+ },
93
+ Sweden: {
94
+ 'OMX Stockholm': [
95
+ { code: 'OMXS30', name: 'OMX Stockholm 30' }
96
+ ]
97
  }
98
  };
99
 
100
+ // ---------- Selections ----------
101
  selectedCountry = '';
102
  selectedExchange = '';
103
  selectedIndexName = '';
104
+
105
+ /** Global selection used for analysis (symbols only) */
106
  selectedCompanies: string[] = [];
 
107
 
108
+ /** Current index selection bound to <mat-select> (symbols only) */
109
+ indexSelected: string[] = [];
110
+
111
+ /** Names for chips (search picks only): symbol -> company name */
112
+ private selectedNameMap = new Map<string, string>();
113
+
114
+ /** Symbols picked via SEARCH only (used to show chips) */
115
+ private searchSelected = new Set<string>();
116
+
117
+ selectedTradingType = 'swing';
118
+
119
+ // ---------- Derived lists for the index path ----------
120
  countries = Object.keys(this.markets);
121
  exchanges: string[] = [];
122
  indices: IndexRef[] = [];
123
  companies: Company[] = [];
124
 
125
+ // Fast lookup & membership for current index
126
+ private companiesBySymbol = new Map<string, string>(); // symbol -> name
127
+ private currentIndexSymbols: Set<string> = new Set<string>(); // membership only
128
+
129
+ // ---------- UI flags ----------
130
  allSelected = false;
131
+ loadingCompanies = false;
132
 
133
+ constructor(
134
+ private http: HttpClient,
135
+ private marketService: MarketService,
136
+ private router: Router,
137
+ private snackBar: MatSnackBar
138
+ ) { }
139
 
140
+ // ---------- Init ----------
141
  ngOnInit(): void {
142
+ // Wire the search box to /searchcompanies
143
  this.filteredCompanies$ = this.tickerCtrl.valueChanges.pipe(
144
  startWith(''),
145
+ debounceTime(200),
146
+ map((q:any) => (q || '').trim()),
147
+ switchMap((query:any) => {
148
+ if (!query) return of([] as SearchHit[]);
149
+ this.globalLoading = true;
150
+ const params = new HttpParams().set('q', query).set('limit', '50');
151
+ return this.http
152
+ .get<{ items: SearchHit[] }>(`${this.API}/searchcompanies`, { params })
153
+ .pipe(
154
+ map((res:any) => res?.items ?? []),
155
+ finalize(() => (this.globalLoading = false))
156
+ );
157
+ })
158
  );
159
  }
160
 
161
+ // ---------- Search selection ----------
162
+ onPickTicker(hit: SearchHit): void {
163
+ // Add to global selection and mark as search-picked
164
+ this.addToSelected(hit.symbol, hit.company);
165
+ this.searchSelected.add(hit.symbol);
 
 
 
166
 
167
+ // If this symbol is in the currently loaded index, also reflect in dropdown
168
+ if (this.currentIndexSymbols.has(hit.symbol) && !this.indexSelected.includes(hit.symbol)) {
169
+ this.indexSelected = [...this.indexSelected, hit.symbol];
170
  }
171
+
172
+ this.allSelected = this.isAllSelected();
173
+ this.tickerCtrl.setValue(''); // clear
174
  }
175
 
176
+ clearTickerSearch(): void {
177
+ this.tickerCtrl.setValue('');
 
178
  }
179
 
180
+ // ---------- Country / Exchange / Index handlers ----------
181
  onCountryChange(): void {
182
  this.error = null;
183
  this.selectedExchange = '';
184
  this.selectedIndexName = '';
 
 
 
 
185
  this.exchanges = this.selectedCountry ? Object.keys(this.markets[this.selectedCountry]) : [];
186
  this.indices = [];
187
+ this.companies = [];
188
+ this.resetCompanyLookup();
189
+ this.indexSelected = []; // reset dropdown model
190
  }
191
 
192
  onExchangeChange(): void {
193
  this.error = null;
194
  this.selectedIndexName = '';
 
 
 
 
195
  this.indices = this.markets[this.selectedCountry]?.[this.selectedExchange] ?? [];
196
+ this.companies = [];
197
+ this.resetCompanyLookup();
198
+ this.indexSelected = []; // reset dropdown model
199
  }
200
 
201
  onIndexChange(): void {
 
207
 
208
  const indexCode = (ref?.code || this.deriveIndexCode(this.selectedIndexName)).toUpperCase();
209
 
 
210
  this.companies = [];
211
+ this.resetCompanyLookup();
212
 
213
  if (!indexCode) {
214
  this.error = 'Unable to resolve index code from selection.';
 
217
  this.fetchCompanies(indexCode);
218
  }
219
 
220
+ // ---------- Multi-select model change (merge with global, no chips for dropdown) ----------
221
+ onCompaniesModelChange(newSymbols: string[]): void {
222
+ this.indexSelected = [...newSymbols];
223
+
224
+ // Recompute global: (global - currentIndex) ∪ indexSelected
225
+ const others = this.selectedCompanies.filter(s => !this.currentIndexSymbols.has(s));
226
+ this.selectedCompanies = Array.from(new Set([...others, ...this.indexSelected]));
227
+
228
+ // If something was deselected via dropdown, also remove its chip if it was a search-pick
229
+ for (const s of [...this.searchSelected]) {
230
+ if (!this.selectedCompanies.includes(s)) this.searchSelected.delete(s);
231
+ }
232
+
233
+ this.allSelected = this.isAllSelected();
234
+ }
235
+
236
+ // ---------- Select All only for current index ----------
237
  toggleSelectAll(): void {
238
  this.allSelected = !this.allSelected;
239
+
240
+ if (this.allSelected) {
241
+ // Select all in current index (chips are not added here)
242
+ this.indexSelected = Array.from(this.currentIndexSymbols);
243
+ } else {
244
+ // Deselect all current index symbols from global and chips
245
+ const current = Array.from(this.currentIndexSymbols);
246
+ this.indexSelected = [];
247
+ this.selectedCompanies = this.selectedCompanies.filter(s => !this.currentIndexSymbols.has(s));
248
+ for (const s of current) this.searchSelected.delete(s);
249
+ }
250
+
251
+ // Keep any selections from other indices
252
+ const others = this.selectedCompanies.filter(s => !this.currentIndexSymbols.has(s));
253
+ this.selectedCompanies = Array.from(new Set([...others, ...this.indexSelected]));
254
  }
255
+
256
  isAllSelected(): boolean {
257
+ if (this.companies.length === 0) return false;
258
+ const selectedSet = new Set(this.indexSelected);
259
+ return this.companies.every(c => selectedSet.has(c.symbol));
260
  }
 
261
 
262
+ trackBySymbol(_: number, c: { symbol: string }): string {
263
+ return c.symbol;
264
+ }
265
+
266
+ // ---------- Backend: fetch constituents for an index ----------
267
  private fetchCompanies(indexCode: string): void {
268
  this.loadingCompanies = true;
269
  const params = new HttpParams().set('code', indexCode);
 
272
  `${this.API}/getcompanies`,
273
  { params }
274
  ).subscribe({
275
+ next: (res:any) => {
276
  this.error = null;
277
  this.companies = res?.constituents ?? [];
278
+
279
+ // Build lookups for this index
280
+ this.companiesBySymbol.clear();
281
+ this.currentIndexSymbols = new Set<string>();
282
+ for (const c of this.companies) {
283
+ this.companiesBySymbol.set(c.symbol, c.company);
284
+ this.currentIndexSymbols.add(c.symbol);
285
+ }
286
+
287
+ // Seed dropdown selection with intersection of global selection & current index
288
+ const selectedSet = new Set(this.selectedCompanies);
289
+ this.indexSelected = this.companies
290
+ .map(c => c.symbol)
291
+ .filter(sym => selectedSet.has(sym));
292
+
293
+ // Do NOT set names here (chips are for search picks only)
294
  this.allSelected = this.isAllSelected();
295
  this.loadingCompanies = false;
296
  },
297
+ error: (err:any) => {
298
  console.error('GET /getcompanies failed', err);
299
  this.error = (err?.error?.error as string) || 'Failed to load companies.';
300
  this.companies = [];
301
+ this.companiesBySymbol.clear();
302
+ this.currentIndexSymbols = new Set<string>();
303
+ this.indexSelected = [];
304
  this.allSelected = false;
305
  this.loadingCompanies = false;
306
  }
307
  });
308
  }
309
 
310
+ // ---------- Helpers ----------
311
  private deriveIndexCode(name: string): string {
312
  return (name || '')
313
  .toUpperCase()
 
317
  .replace(/[^A-Z0-9]/g, '');
318
  }
319
 
320
+ getMarket(): string[] {
321
+ return this.exchanges;
322
+ }
323
+
324
+ getMarketDivision(): string[] {
325
+ return this.indices.map(i => i.name);
326
+ }
327
+
328
+ getCompanies(): Company[] {
329
+ return this.companies;
330
+ }
331
+
332
+ private addToSelected(symbol: string, companyName: string): void {
333
+ if (!this.selectedCompanies.includes(symbol)) {
334
+ this.selectedCompanies = [...this.selectedCompanies, symbol];
335
+ }
336
+ // Store name only for search picks (chips)
337
+ if (companyName) this.selectedNameMap.set(symbol, companyName);
338
+ }
339
+
340
+ private resetCompanyLookup(): void {
341
+ this.companiesBySymbol.clear();
342
+ this.currentIndexSymbols = new Set<string>();
343
+ this.allSelected = false;
344
+ }
345
 
346
+ // Chips show only search-picked symbols
347
+ get selectedChips(): CompanyOption[] {
348
+ return Array.from(this.searchSelected).map(s => ({
349
+ symbol: s,
350
+ company: this.selectedNameMap.get(s) || s
351
+ }));
352
+ }
353
+
354
+ // ---------- Analysis ----------
355
  canRunAnalysis(): boolean {
356
+ return !!this.selectedTradingType && this.selectedCompanies.length > 0;
357
+ }
358
+
359
+ submit(): void {
360
+ if (this.selectedTradingType == 'swing') {
361
+ const symbols = [...new Set(this.selectedCompanies)];
362
+ if (!this.selectedTradingType) {
363
+ this.error = 'Please select a trading type.';
364
+ return;
365
+ }
366
+ if (symbols.length === 0) {
367
+ this.error = 'Please select at least one company or ticker.';
368
+ return;
369
+ }
370
+
371
+ this.error = null;
372
+ this.loading = true;
373
+
374
+ this.marketService.getAnalyseResult(symbols)
375
+ .pipe(finalize(() => (this.loading = false)))
376
+ .subscribe({
377
+ next: (res:any) => this.router.navigate(['/analysepage'], { state: { result: res } }),
378
+ error: (err:any) => {
379
+ console.error('API error:', err);
380
+ this.error = 'Analysis failed. Please try again.';
381
+ }
382
+ });
383
+ } else {
384
+ this.openSnackBar();
385
+ }
386
+
387
  }
388
 
389
  resetAll(): void {
390
  this.error = null;
391
 
392
+ // Filters
393
  this.selectedCountry = '';
394
  this.selectedExchange = '';
395
  this.selectedIndexName = '';
 
396
  this.exchanges = [];
397
  this.indices = [];
398
  this.companies = [];
399
+ this.resetCompanyLookup();
400
+
401
+ // Unified selection
402
+ this.selectedCompanies = [];
403
+ this.indexSelected = [];
404
+ this.selectedNameMap.clear();
405
+ this.searchSelected.clear();
406
 
407
  // Search
408
  this.tickerCtrl.setValue('');
 
 
 
 
409
  }
410
 
411
+ // Chips remove (search picks only)
412
+ onRemoveChip(symbol: string): void {
413
+ this.searchSelected.delete(symbol); // hide chip
414
+ this.selectedCompanies = this.selectedCompanies.filter(s => s !== symbol); // unselect globally
 
 
415
 
416
+ // If symbol is part of the current index, reflect in dropdown
417
+ if (this.currentIndexSymbols.has(symbol)) {
418
+ this.indexSelected = this.indexSelected.filter(s => s !== symbol);
419
+ this.allSelected = this.isAllSelected();
 
 
 
420
  }
421
+ }
422
 
423
+ openSnackBar(): void {
424
+ const ref = this.snackBar.open('Under Constructon', 'Close', {
425
+ duration: 3000,
426
+ horizontalPosition: 'center',
427
+ verticalPosition: 'bottom',
428
+ panelClass: ['snackbar-success']
429
+ });
430
+
431
+ ref.onAction().subscribe(() => {
432
+
433
+ });
 
434
  }
435
  }