running prettier for cleanup
This commit is contained in:
parent
fe7733aeb5
commit
d85cd58acd
151 changed files with 29158 additions and 27966 deletions
|
|
@ -1,221 +1,259 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { BacktestResult } from '../../../services/strategy.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-trades-table',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatTableModule,
|
||||
MatSortModule,
|
||||
MatPaginatorModule,
|
||||
MatCardModule,
|
||||
MatIconModule
|
||||
],
|
||||
template: `
|
||||
<mat-card class="trades-card">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Trades</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<table mat-table [dataSource]="displayedTrades" matSort (matSortChange)="sortData($event)" class="trades-table">
|
||||
|
||||
<!-- Symbol Column -->
|
||||
<ng-container matColumnDef="symbol">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Symbol </th>
|
||||
<td mat-cell *matCellDef="let trade"> {{trade.symbol}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Entry Date Column -->
|
||||
<ng-container matColumnDef="entryTime">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Entry Time </th>
|
||||
<td mat-cell *matCellDef="let trade"> {{formatDate(trade.entryTime)}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Entry Price Column -->
|
||||
<ng-container matColumnDef="entryPrice">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Entry Price </th>
|
||||
<td mat-cell *matCellDef="let trade"> {{formatCurrency(trade.entryPrice)}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Exit Date Column -->
|
||||
<ng-container matColumnDef="exitTime">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Exit Time </th>
|
||||
<td mat-cell *matCellDef="let trade"> {{formatDate(trade.exitTime)}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Exit Price Column -->
|
||||
<ng-container matColumnDef="exitPrice">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Exit Price </th>
|
||||
<td mat-cell *matCellDef="let trade"> {{formatCurrency(trade.exitPrice)}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Quantity Column -->
|
||||
<ng-container matColumnDef="quantity">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Quantity </th>
|
||||
<td mat-cell *matCellDef="let trade"> {{trade.quantity}} </td>
|
||||
</ng-container>
|
||||
|
||||
<!-- P&L Column -->
|
||||
<ng-container matColumnDef="pnl">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> P&L </th>
|
||||
<td mat-cell *matCellDef="let trade"
|
||||
[ngClass]="{'positive': trade.pnl > 0, 'negative': trade.pnl < 0}">
|
||||
{{formatCurrency(trade.pnl)}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- P&L Percent Column -->
|
||||
<ng-container matColumnDef="pnlPercent">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> P&L % </th>
|
||||
<td mat-cell *matCellDef="let trade"
|
||||
[ngClass]="{'positive': trade.pnlPercent > 0, 'negative': trade.pnlPercent < 0}">
|
||||
{{formatPercent(trade.pnlPercent)}}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[length]="totalTrades"
|
||||
[pageSize]="pageSize"
|
||||
[pageSizeOptions]="[5, 10, 25, 50]"
|
||||
(page)="pageChange($event)"
|
||||
aria-label="Select page">
|
||||
</mat-paginator>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
`,
|
||||
styles: `
|
||||
.trades-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.trades-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.mat-column-pnl, .mat-column-pnlPercent {
|
||||
text-align: right;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.positive {
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.negative {
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
.mat-mdc-row:hover {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
`
|
||||
})
|
||||
export class TradesTableComponent {
|
||||
@Input() set backtestResult(value: BacktestResult | undefined) {
|
||||
if (value) {
|
||||
this._backtestResult = value;
|
||||
this.updateDisplayedTrades();
|
||||
}
|
||||
}
|
||||
|
||||
get backtestResult(): BacktestResult | undefined {
|
||||
return this._backtestResult;
|
||||
}
|
||||
|
||||
private _backtestResult?: BacktestResult;
|
||||
|
||||
// Table configuration
|
||||
displayedColumns: string[] = [
|
||||
'symbol', 'entryTime', 'entryPrice', 'exitTime',
|
||||
'exitPrice', 'quantity', 'pnl', 'pnlPercent'
|
||||
];
|
||||
|
||||
// Pagination
|
||||
pageSize = 10;
|
||||
currentPage = 0;
|
||||
displayedTrades: any[] = [];
|
||||
|
||||
get totalTrades(): number {
|
||||
return this._backtestResult?.trades.length || 0;
|
||||
}
|
||||
|
||||
// Sort the trades
|
||||
sortData(sort: Sort): void {
|
||||
if (!sort.active || sort.direction === '') {
|
||||
this.updateDisplayedTrades();
|
||||
return;
|
||||
}
|
||||
|
||||
const data = this._backtestResult?.trades.slice() || [];
|
||||
|
||||
this.displayedTrades = data.sort((a, b) => {
|
||||
const isAsc = sort.direction === 'asc';
|
||||
switch (sort.active) {
|
||||
case 'symbol': return this.compare(a.symbol, b.symbol, isAsc);
|
||||
case 'entryTime': return this.compare(new Date(a.entryTime).getTime(), new Date(b.entryTime).getTime(), isAsc);
|
||||
case 'entryPrice': return this.compare(a.entryPrice, b.entryPrice, isAsc);
|
||||
case 'exitTime': return this.compare(new Date(a.exitTime).getTime(), new Date(b.exitTime).getTime(), isAsc);
|
||||
case 'exitPrice': return this.compare(a.exitPrice, b.exitPrice, isAsc);
|
||||
case 'quantity': return this.compare(a.quantity, b.quantity, isAsc);
|
||||
case 'pnl': return this.compare(a.pnl, b.pnl, isAsc);
|
||||
case 'pnlPercent': return this.compare(a.pnlPercent, b.pnlPercent, isAsc);
|
||||
default: return 0;
|
||||
}
|
||||
}).slice(this.currentPage * this.pageSize, (this.currentPage + 1) * this.pageSize);
|
||||
}
|
||||
|
||||
// Handle page changes
|
||||
pageChange(event: PageEvent): void {
|
||||
this.pageSize = event.pageSize;
|
||||
this.currentPage = event.pageIndex;
|
||||
this.updateDisplayedTrades();
|
||||
}
|
||||
|
||||
// Update displayed trades based on current page and page size
|
||||
updateDisplayedTrades(): void {
|
||||
if (this._backtestResult) {
|
||||
this.displayedTrades = this._backtestResult.trades.slice(
|
||||
this.currentPage * this.pageSize,
|
||||
(this.currentPage + 1) * this.pageSize
|
||||
);
|
||||
} else {
|
||||
this.displayedTrades = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Helper methods for formatting
|
||||
formatDate(date: Date | string): string {
|
||||
return new Date(date).toLocaleString();
|
||||
}
|
||||
|
||||
formatCurrency(value: number): string {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
formatPercent(value: number): string {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'percent',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
private compare(a: number | string, b: number | string, isAsc: boolean): number {
|
||||
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
|
||||
}
|
||||
}
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { BacktestResult } from '../../../services/strategy.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-trades-table',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatTableModule,
|
||||
MatSortModule,
|
||||
MatPaginatorModule,
|
||||
MatCardModule,
|
||||
MatIconModule,
|
||||
],
|
||||
template: `
|
||||
<mat-card class="trades-card">
|
||||
<mat-card-header>
|
||||
<mat-card-title>Trades</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<table
|
||||
mat-table
|
||||
[dataSource]="displayedTrades"
|
||||
matSort
|
||||
(matSortChange)="sortData($event)"
|
||||
class="trades-table"
|
||||
>
|
||||
<!-- Symbol Column -->
|
||||
<ng-container matColumnDef="symbol">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Symbol</th>
|
||||
<td mat-cell *matCellDef="let trade">{{ trade.symbol }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Entry Date Column -->
|
||||
<ng-container matColumnDef="entryTime">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Entry Time</th>
|
||||
<td mat-cell *matCellDef="let trade">{{ formatDate(trade.entryTime) }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Entry Price Column -->
|
||||
<ng-container matColumnDef="entryPrice">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Entry Price</th>
|
||||
<td mat-cell *matCellDef="let trade">{{ formatCurrency(trade.entryPrice) }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Exit Date Column -->
|
||||
<ng-container matColumnDef="exitTime">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Exit Time</th>
|
||||
<td mat-cell *matCellDef="let trade">{{ formatDate(trade.exitTime) }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Exit Price Column -->
|
||||
<ng-container matColumnDef="exitPrice">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Exit Price</th>
|
||||
<td mat-cell *matCellDef="let trade">{{ formatCurrency(trade.exitPrice) }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Quantity Column -->
|
||||
<ng-container matColumnDef="quantity">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Quantity</th>
|
||||
<td mat-cell *matCellDef="let trade">{{ trade.quantity }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- P&L Column -->
|
||||
<ng-container matColumnDef="pnl">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>P&L</th>
|
||||
<td
|
||||
mat-cell
|
||||
*matCellDef="let trade"
|
||||
[ngClass]="{ positive: trade.pnl > 0, negative: trade.pnl < 0 }"
|
||||
>
|
||||
{{ formatCurrency(trade.pnl) }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- P&L Percent Column -->
|
||||
<ng-container matColumnDef="pnlPercent">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>P&L %</th>
|
||||
<td
|
||||
mat-cell
|
||||
*matCellDef="let trade"
|
||||
[ngClass]="{ positive: trade.pnlPercent > 0, negative: trade.pnlPercent < 0 }"
|
||||
>
|
||||
{{ formatPercent(trade.pnlPercent) }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator
|
||||
[length]="totalTrades"
|
||||
[pageSize]="pageSize"
|
||||
[pageSizeOptions]="[5, 10, 25, 50]"
|
||||
(page)="pageChange($event)"
|
||||
aria-label="Select page"
|
||||
>
|
||||
</mat-paginator>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
`,
|
||||
styles: `
|
||||
.trades-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.trades-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.mat-column-pnl,
|
||||
.mat-column-pnlPercent {
|
||||
text-align: right;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.positive {
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
.negative {
|
||||
color: #f44336;
|
||||
}
|
||||
|
||||
.mat-mdc-row:hover {
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
`,
|
||||
})
|
||||
export class TradesTableComponent {
|
||||
@Input() set backtestResult(value: BacktestResult | undefined) {
|
||||
if (value) {
|
||||
this._backtestResult = value;
|
||||
this.updateDisplayedTrades();
|
||||
}
|
||||
}
|
||||
|
||||
get backtestResult(): BacktestResult | undefined {
|
||||
return this._backtestResult;
|
||||
}
|
||||
|
||||
private _backtestResult?: BacktestResult;
|
||||
|
||||
// Table configuration
|
||||
displayedColumns: string[] = [
|
||||
'symbol',
|
||||
'entryTime',
|
||||
'entryPrice',
|
||||
'exitTime',
|
||||
'exitPrice',
|
||||
'quantity',
|
||||
'pnl',
|
||||
'pnlPercent',
|
||||
];
|
||||
|
||||
// Pagination
|
||||
pageSize = 10;
|
||||
currentPage = 0;
|
||||
displayedTrades: any[] = [];
|
||||
|
||||
get totalTrades(): number {
|
||||
return this._backtestResult?.trades.length || 0;
|
||||
}
|
||||
|
||||
// Sort the trades
|
||||
sortData(sort: Sort): void {
|
||||
if (!sort.active || sort.direction === '') {
|
||||
this.updateDisplayedTrades();
|
||||
return;
|
||||
}
|
||||
|
||||
const data = this._backtestResult?.trades.slice() || [];
|
||||
|
||||
this.displayedTrades = data
|
||||
.sort((a, b) => {
|
||||
const isAsc = sort.direction === 'asc';
|
||||
switch (sort.active) {
|
||||
case 'symbol':
|
||||
return this.compare(a.symbol, b.symbol, isAsc);
|
||||
case 'entryTime':
|
||||
return this.compare(
|
||||
new Date(a.entryTime).getTime(),
|
||||
new Date(b.entryTime).getTime(),
|
||||
isAsc
|
||||
);
|
||||
case 'entryPrice':
|
||||
return this.compare(a.entryPrice, b.entryPrice, isAsc);
|
||||
case 'exitTime':
|
||||
return this.compare(
|
||||
new Date(a.exitTime).getTime(),
|
||||
new Date(b.exitTime).getTime(),
|
||||
isAsc
|
||||
);
|
||||
case 'exitPrice':
|
||||
return this.compare(a.exitPrice, b.exitPrice, isAsc);
|
||||
case 'quantity':
|
||||
return this.compare(a.quantity, b.quantity, isAsc);
|
||||
case 'pnl':
|
||||
return this.compare(a.pnl, b.pnl, isAsc);
|
||||
case 'pnlPercent':
|
||||
return this.compare(a.pnlPercent, b.pnlPercent, isAsc);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
})
|
||||
.slice(this.currentPage * this.pageSize, (this.currentPage + 1) * this.pageSize);
|
||||
}
|
||||
|
||||
// Handle page changes
|
||||
pageChange(event: PageEvent): void {
|
||||
this.pageSize = event.pageSize;
|
||||
this.currentPage = event.pageIndex;
|
||||
this.updateDisplayedTrades();
|
||||
}
|
||||
|
||||
// Update displayed trades based on current page and page size
|
||||
updateDisplayedTrades(): void {
|
||||
if (this._backtestResult) {
|
||||
this.displayedTrades = this._backtestResult.trades.slice(
|
||||
this.currentPage * this.pageSize,
|
||||
(this.currentPage + 1) * this.pageSize
|
||||
);
|
||||
} else {
|
||||
this.displayedTrades = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Helper methods for formatting
|
||||
formatDate(date: Date | string): string {
|
||||
return new Date(date).toLocaleString();
|
||||
}
|
||||
|
||||
formatCurrency(value: number): string {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
formatPercent(value: number): string {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'percent',
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
private compare(a: number | string, b: number | string, isAsc: boolean): number {
|
||||
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue