Spaces:
Running
Running
Oviya
commited on
Commit
·
df4a76e
1
Parent(s):
b5872a0
update toolspage
Browse files
public/globe.png
ADDED
|
Git LFS Details
|
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)]="
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
<!--
|
| 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
|
| 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
|
| 96 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
</mat-option>
|
| 98 |
</mat-autocomplete>
|
| 99 |
|
| 100 |
-
<!--
|
| 101 |
-
<mat-chip-listbox *ngIf="
|
| 102 |
-
<mat-chip-row *ngFor="let t of
|
| 103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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/
|
| 11 |
-
|
| 12 |
}
|
| 13 |
|
| 14 |
.filters-content {
|
|
@@ -21,12 +21,12 @@
|
|
| 21 |
.filters-card {
|
| 22 |
width: 30vw;
|
| 23 |
padding: 1vw;
|
| 24 |
-
|
| 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
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 21 |
-
|
|
|
|
|
|
|
| 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 |
-
// ----------
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 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 |
-
// ----------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
countries = Object.keys(this.markets);
|
| 107 |
exchanges: string[] = [];
|
| 108 |
indices: IndexRef[] = [];
|
| 109 |
companies: Company[] = [];
|
| 110 |
|
| 111 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
allSelected = false;
|
| 113 |
-
loadingCompanies = false;
|
| 114 |
|
| 115 |
-
constructor(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
|
|
|
|
| 117 |
ngOnInit(): void {
|
|
|
|
| 118 |
this.filteredCompanies$ = this.tickerCtrl.valueChanges.pipe(
|
| 119 |
startWith(''),
|
| 120 |
-
debounceTime(
|
| 121 |
-
map(q =>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
);
|
| 123 |
}
|
| 124 |
|
| 125 |
-
//
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
c.symbol.toLowerCase().includes(q) || c.company.toLowerCase().includes(q)
|
| 131 |
-
).slice(0, 20);
|
| 132 |
-
}
|
| 133 |
|
| 134 |
-
|
| 135 |
-
if (
|
| 136 |
-
this.
|
| 137 |
}
|
| 138 |
-
|
|
|
|
|
|
|
| 139 |
}
|
| 140 |
|
| 141 |
-
clearTickerSearch(): void {
|
| 142 |
-
|
| 143 |
-
this.pickedTickers = this.pickedTickers.filter(t => t.symbol !== symbol);
|
| 144 |
}
|
| 145 |
|
| 146 |
-
//
|
| 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.
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
}
|
|
|
|
| 193 |
isAllSelected(): boolean {
|
| 194 |
-
|
|
|
|
|
|
|
| 195 |
}
|
| 196 |
-
trackBySymbol(_: number, c: { symbol: string }) { return c.symbol; }
|
| 197 |
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 211 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.
|
|
|
|
|
|
|
| 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[] {
|
| 236 |
-
|
| 237 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
|
| 239 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
canRunAnalysis(): boolean {
|
| 241 |
-
|
| 242 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
}
|
| 244 |
|
| 245 |
resetAll(): void {
|
| 246 |
this.error = null;
|
| 247 |
|
| 248 |
-
//
|
| 249 |
this.selectedCountry = '';
|
| 250 |
this.selectedExchange = '';
|
| 251 |
this.selectedIndexName = '';
|
| 252 |
-
this.selectedCompanies = [];
|
| 253 |
this.exchanges = [];
|
| 254 |
this.indices = [];
|
| 255 |
this.companies = [];
|
| 256 |
-
this.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
|
| 258 |
// Search
|
| 259 |
this.tickerCtrl.setValue('');
|
| 260 |
-
this.pickedTickers = [];
|
| 261 |
-
|
| 262 |
-
// Common
|
| 263 |
-
this.selectedTradingType = '';
|
| 264 |
}
|
| 265 |
|
| 266 |
-
//
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
? this.selectedCompanies
|
| 271 |
-
: this.pickedTickers.map(t => t.symbol);
|
| 272 |
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
if (symbols.length === 0) {
|
| 278 |
-
this.error = 'Please select at least one company or ticker.';
|
| 279 |
-
return;
|
| 280 |
}
|
|
|
|
| 281 |
|
| 282 |
-
|
| 283 |
-
this.
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 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 |
}
|