mirror of
https://github.com/Dansen999/at-certification-stock.git
synced 2026-01-11 21:43:34 +00:00
Implemented features.
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
.mat-card {
|
||||
margin: 1rem 0;
|
||||
height: 14rem;
|
||||
line-height: 2rem;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto;
|
||||
padding: 10px;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<mat-card *ngIf="loaded; else loading">
|
||||
<ng-container>
|
||||
<mat-card-title>{{company.description}} ({{company.symbol}})</mat-card-title>
|
||||
<mat-card-content>
|
||||
<div class="grid-container content">
|
||||
<div class="grid-item" *ngFor="let insiderSentiment of insiderSentiments ">
|
||||
<app-sentiment [month]="insiderSentiment.month"
|
||||
[change]="insiderSentiment.change"
|
||||
[mspr]="insiderSentiment.mspr"></app-sentiment>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</ng-container>
|
||||
</mat-card>
|
||||
|
||||
<ng-template #loading>
|
||||
<mat-card>
|
||||
<mat-card-title>Loading...</mat-card-title>
|
||||
<mat-card-content></mat-card-content>
|
||||
</mat-card>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SentimentCardComponent } from './sentiment-card.component';
|
||||
|
||||
describe('SentimentCardComponent', () => {
|
||||
let component: SentimentCardComponent;
|
||||
let fixture: ComponentFixture<SentimentCardComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SentimentCardComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SentimentCardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
||||
import {Company, InsiderSentimentData} from "../../model/company-data";
|
||||
import {FinnhubService} from "../../services/finnhub.service";
|
||||
import {Subscription} from "rxjs";
|
||||
|
||||
@Component({
|
||||
selector: 'app-sentiment-card',
|
||||
templateUrl: './sentiment-card.component.html',
|
||||
styleUrls: ['./sentiment-card.component.css']
|
||||
})
|
||||
export class SentimentCardComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input()
|
||||
symbol = '';
|
||||
|
||||
loaded = false;
|
||||
company!: Company;
|
||||
insiderSentiments!: Array<InsiderSentimentData>;
|
||||
|
||||
private loadedSubs = 0;
|
||||
private subs: Subscription[] = [];
|
||||
|
||||
constructor(private finnhubService: FinnhubService) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.subs.push(this.finnhubService.getCompany(this.symbol).subscribe(value => {
|
||||
this.company = value;
|
||||
this.loaded = ++this.loadedSubs == this.subs.length;
|
||||
}));
|
||||
|
||||
let current = new Date()
|
||||
let from = new Date(current.getFullYear(), current.getMonth() - 3, 1);
|
||||
let to = new Date(current.getFullYear(), current.getMonth() - 1, 1);
|
||||
|
||||
this.subs.push(this.finnhubService.getInsideSentiment(this.symbol, from, to).subscribe(value => {
|
||||
this.insiderSentiments = value.data;
|
||||
this.loaded = ++this.loadedSubs == this.subs.length;
|
||||
}));
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subs.forEach(s => s.unsubscribe());
|
||||
}
|
||||
}
|
||||
10
src/app/components/sentinment/sentiment.component.css
Normal file
10
src/app/components/sentinment/sentiment.component.css
Normal file
@@ -0,0 +1,10 @@
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.grid-item .label {
|
||||
font-weight: bold;
|
||||
margin: 1rem;
|
||||
}
|
||||
11
src/app/components/sentinment/sentiment.component.html
Normal file
11
src/app/components/sentinment/sentiment.component.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<div class="grid-container ">
|
||||
<div class="grid-item">
|
||||
<p><span class="label">{{month | month }}</span></p>
|
||||
<p><span class="label">Change:</span><span class="value">{{change | number: '1.0-0'}}</span></p>
|
||||
<p><span class="label">MSPR:</span><span class="value">{{mspr | number: '1.2-2'}}</span></p>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<app-tendency [value]="change"></app-tendency>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
23
src/app/components/sentinment/sentiment.component.spec.ts
Normal file
23
src/app/components/sentinment/sentiment.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SentimentComponent } from './sentiment.component';
|
||||
|
||||
describe('SentinmentComponent', () => {
|
||||
let component: SentimentComponent;
|
||||
let fixture: ComponentFixture<SentimentComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SentimentComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SentimentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
25
src/app/components/sentinment/sentiment.component.ts
Normal file
25
src/app/components/sentinment/sentiment.component.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-sentiment',
|
||||
templateUrl: './sentiment.component.html',
|
||||
styleUrls: ['./sentiment.component.css']
|
||||
})
|
||||
export class SentimentComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
month!: number;
|
||||
|
||||
@Input()
|
||||
change!: number;
|
||||
|
||||
@Input()
|
||||
mspr!: number;
|
||||
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,24 @@
|
||||
.mat-card {
|
||||
margin: 1rem;
|
||||
margin: 1rem 0;
|
||||
height: 14rem;
|
||||
line-height: 2rem;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.mat-card-title .mat-icon {
|
||||
a.remove {
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mat-grid-tile .mat-icon {
|
||||
font-size: 4rem;
|
||||
height: 4rem;
|
||||
width: 4rem;
|
||||
.grid-container {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.grid-item .label {
|
||||
font-weight: bold;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +1,54 @@
|
||||
<mat-card>
|
||||
<mat-card *ngIf="loaded; else loading">
|
||||
|
||||
<mat-card-title>{{company.description}} ({{company.symbol}})
|
||||
<mat-icon (click)="remove()" fontIcon="close"></mat-icon>
|
||||
<a (click)="remove()" id="{{'remove'+ company.symbol}}" class="remove">
|
||||
<mat-icon fontIcon="close"></mat-icon>
|
||||
</a>
|
||||
</mat-card-title>
|
||||
|
||||
|
||||
<mat-card-content>
|
||||
<ng-container *ngIf="getQuote() | async; let quote">
|
||||
<mat-grid-list cols="4" rowHeight="4:1">
|
||||
|
||||
<mat-grid-tile colspan="2"></mat-grid-tile>
|
||||
<mat-grid-tile colspan="2" rowspan="3">
|
||||
<mat-icon *ngIf="quote.dp>0" color="primary" fontIcon="arrow_upward"></mat-icon>
|
||||
<mat-icon *ngIf="quote.dp<0" color="warn" fontIcon="arrow_downward"></mat-icon>
|
||||
<mat-icon *ngIf="quote.dp==0" fontIcon="east"></mat-icon>
|
||||
</mat-grid-tile>
|
||||
<div class="grid-container content">
|
||||
<div class="grid-item">
|
||||
<div class="grid-container values">
|
||||
<div class="grid-item">
|
||||
<span class="label">Change today:</span>
|
||||
<span class="value">{{quote.dp / 100 | percent: '1.1-1'}}</span>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<span class="label">Opening price:</span>
|
||||
<span class="value">{{quote.o | currency: 'USD'}}</span>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<span class="label">Current price:</span>
|
||||
<span class="value">{{quote.c | currency: 'USD'}}</span>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<span class="label">High price:</span>
|
||||
<span class="value">{{quote.h | currency: 'USD'}}</span>
|
||||
</div>
|
||||
|
||||
<mat-grid-tile><p>Change today: {{quote.dp / 100 | percent: '1.1-1'}}</p></mat-grid-tile>
|
||||
<mat-grid-tile><p>Opening price: {{quote.o | currency: 'USD'}}</p></mat-grid-tile>
|
||||
<mat-grid-tile><p>Current price: {{quote.c | currency: 'USD'}}</p></mat-grid-tile>
|
||||
<mat-grid-tile><p>High price: {{quote.h | currency: 'USD'}}</p></mat-grid-tile>
|
||||
</mat-grid-list>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<app-tendency [value]="quote.dp"></app-tendency>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions align="end">
|
||||
<button mat-button [routerLink]="'/sentiment/' + company.symbol">Go to social sentiment details</button>
|
||||
<button mat-button
|
||||
id="{{'sentiment'+ company.symbol}}"
|
||||
[routerLink]="'/sentiment/' + symbol">Go to social sentiment details
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</mat-card>
|
||||
|
||||
<ng-template #loading>
|
||||
<mat-card>
|
||||
<mat-card-title>Loading...</mat-card-title>
|
||||
<mat-card-content></mat-card-content>
|
||||
</mat-card>
|
||||
</ng-template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
|
||||
import {FinnhubService} from "../../services/finnhub.service";
|
||||
import {Company, QuoteResponse} from "../../model/company-data";
|
||||
import {Subject, Subscription} from "rxjs";
|
||||
import {Subscription} from "rxjs";
|
||||
import {StorageService} from "../../services/storage.service";
|
||||
|
||||
@Component({
|
||||
@@ -12,9 +12,14 @@ import {StorageService} from "../../services/storage.service";
|
||||
export class StockCardComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input()
|
||||
company = {} as Company;
|
||||
symbol = '';
|
||||
|
||||
private _quote$ = new Subject<QuoteResponse>();
|
||||
loaded = false;
|
||||
company!: Company;
|
||||
quote!: QuoteResponse;
|
||||
|
||||
|
||||
private loadedSubs = 0;
|
||||
private subs: Subscription[] = [];
|
||||
|
||||
constructor(public finnhubService: FinnhubService,
|
||||
@@ -22,19 +27,22 @@ export class StockCardComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.subs.push(this.finnhubService.getQuote(this.company.symbol).subscribe(value => this._quote$.next(value)));
|
||||
this.subs.push(this.finnhubService.getCompany(this.symbol).subscribe(value => {
|
||||
this.company = value;
|
||||
this.loaded = ++this.loadedSubs == this.subs.length;
|
||||
}));
|
||||
this.subs.push(this.finnhubService.getQuote(this.symbol).subscribe(value => {
|
||||
this.quote = value;
|
||||
this.loaded = ++this.loadedSubs == this.subs.length;
|
||||
}));
|
||||
}
|
||||
|
||||
getQuote(): Subject<QuoteResponse> {
|
||||
return this._quote$;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subs.forEach(s => s.unsubscribe());
|
||||
this._quote$.complete();
|
||||
}
|
||||
|
||||
remove() {
|
||||
this.storageService.remove(this.company.symbol);
|
||||
this.storageService.remove(this.symbol);
|
||||
}
|
||||
}
|
||||
|
||||
6
src/app/components/tendency/tendency.component.css
Normal file
6
src/app/components/tendency/tendency.component.css
Normal file
@@ -0,0 +1,6 @@
|
||||
.mat-icon {
|
||||
font-size: 6rem;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
3
src/app/components/tendency/tendency.component.html
Normal file
3
src/app/components/tendency/tendency.component.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<mat-icon *ngIf="value>0" color="primary" fontIcon="arrow_upward"></mat-icon>
|
||||
<mat-icon *ngIf="value<0" color="warn" fontIcon="arrow_downward"></mat-icon>
|
||||
<mat-icon *ngIf="value==0" fontIcon="east"></mat-icon>
|
||||
23
src/app/components/tendency/tendency.component.spec.ts
Normal file
23
src/app/components/tendency/tendency.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TendencyComponent } from './tendency.component';
|
||||
|
||||
describe('TendencyComponent', () => {
|
||||
let component: TendencyComponent;
|
||||
let fixture: ComponentFixture<TendencyComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ TendencyComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TendencyComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
19
src/app/components/tendency/tendency.component.ts
Normal file
19
src/app/components/tendency/tendency.component.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tendency',
|
||||
templateUrl: './tendency.component.html',
|
||||
styleUrls: ['./tendency.component.css']
|
||||
})
|
||||
export class TendencyComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
value!: number;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user