import { Injectable, HttpException, HttpStatus, Logger } from '@nestjs/common';
import { HttpService, } from '@nestjs/axios';
import * as dayjs from 'dayjs';
import { AxiosResponse } from 'axios';
import { log } from 'console';
import * as https from 'https';
@Injectable()
export class DailyEquityService {
private readonly spotwareApiUrl: string;
private readonly apiToken: string;
private readonly xanoEquityUrl: string;
private readonly logger = new Logger(DailyEquityService.name);
private lastFetchedData: any[] = [];
constructor(private readonly httpService: HttpService) {
this.spotwareApiUrl = process.env.SPOTWARE_API_URL;
this.apiToken = process.env.SPOTWARE_API_TOKEN;
this.xanoEquityUrl = process.env.XANO_API_EQUITYURL;
this.httpService.axiosRef.defaults.httpsAgent = new https.Agent({ rejectUnauthorized: false });
}
// Fetch daily equity data from external service
async fetchDailyEquityData(fromDate: string, toDate: string) {
this.logger.log(`Fetching daily equity data from Spotware for date range ${fromDate} to ${toDate}`);
try {
const response: AxiosResponse = await this.httpService
.get(`${this.spotwareApiUrl}/v2/webserv/traders/`, {
params: {
from: fromDate,
to: toDate,
fields: 'login,balance,minEquityDaily,maxEquityDaily',
token: this.apiToken,
},
})
.toPromise();
if (response.status !== 200) {
throw new Error(`Unexpected status code: ${response.status}`);
}
const mappedData = response.data.trader.map((trader) => ({
account: trader.login,
starting_daily_equity: trader.balance.toString(),
sde_date: dayjs().format('YYYY-MM-DD'),
gmt_date: dayjs().toISOString(),
created_at: dayjs().toISOString(),
status: 'pending',
trading_days: '0',
challenge_begins: dayjs().subtract(30, 'days').format('YYYY-MM-DD'),
new_status: 'pending',
}));
this.logger.log('Successfully fetched and mapped daily equity data');
return mappedData;
} catch (error) {
this.logger.error(`Error fetching daily equity data: ${error.message}`);
throw new HttpException(`Failed to fetch daily equity data: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
// Check if data exists in Xano, and then update or create as needed
async checkAndUpdateDailyEquityInXano(equityData: any[]) {
this.logger.log('Starting Xano data check and update process');
// Log to check the equityData content
this.logger.debug(`Received equityData: ${JSON.stringify(equityData)}`);
const results = [];
if (!equityData || equityData.length === 0) {
this.logger.warn('No equity data to process. Exiting function.');
return results; // Return early if there is no data
}
for (const data of equityData) {
try {
this.logger.debug(`Processing account: ${data.account}`);
console.log(`${this.xanoEquityUrl}/${data.account}/`,"hyhy");
const existingDataResponse = await this.httpService
.get(`${this.xanoEquityUrl}/${data.account}/`, {
headers: { 'Content-Type': 'application/json' },
})
.toPromise();
this.logger.debug(`Received response for account ${data.account}: ${JSON.stringify(existingDataResponse.data)}`);
if (existingDataResponse.status === 200) {
this.logger.log(`Account ${data.account} exists in Xano, updating record`);
const updateResponse = await this.httpService
.patch(`${this.xanoEquityUrl}/${data.account}`, data, {
headers: { 'Content-Type': 'application/json' },
})
.toPromise();
this.logger.debug(`Update response for account ${data.account}: ${JSON.stringify(updateResponse.data)}`);
results.push(updateResponse.data);
} else {
this.logger.log(`Account ${data.account} does not exist, creating new record`);
const createResponse = await this.httpService
.post(this.xanoEquityUrl, data, {
headers: { 'Content-Type': 'application/json' },
})
.toPromise();
this.logger.debug(`Create response for account ${data.account}: ${JSON.stringify(createResponse.data)}`);
results.push(createResponse.data);
}
} catch (error) {
if (error.response?.status === 404) {
this.logger.log(`Account ${data.account} not found in Xano, creating new record`);
const createResponse = await this.httpService
.post(this.xanoEquityUrl, data, {
headers: { 'Content-Type': 'application/json' },
})
.toPromise();
this.logger.debug(`Create response for account ${data.account}: ${JSON.stringify(createResponse.data)}`);
results.push(createResponse.data);
} else {
this.logger.error(`Failed to process account ${data.account}: ${error.message}`);
throw new HttpException(`Failed to process account ${data.account}: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
this.logger.log('Completed Xano data check and update process');
return results;
}
// Update the equity for traders daily
async updateDailyEquityForTraders() {
const fromDate = dayjs().subtract(1, 'day').startOf('day').format('YYYY-MM-DDTHH:mm:ss.SSS');
const toDate = dayjs().startOf('day').format('YYYY-MM-DDTHH:mm:ss.SSS');
this.logger.log(`Starting daily equity update for traders from ${fromDate} to ${toDate}`);
try {
const equityData = await this.fetchDailyEquityData(fromDate, toDate);
if (this.hasDataChanged(equityData)) {
this.logger.log('Data has changed; proceeding with update');
const result = await this.checkAndUpdateDailyEquityInXano(equityData);
this.lastFetchedData = equityData; // Update the cache
return result;
} else {
this.logger.log('No changes in data; skipping Xano update');
return { message: 'No changes detected; update skipped.' };
}
} catch (error) {
this.logger.error(`Failed to update daily equity for traders: ${error.message}`);
throw new HttpException(`Failed to update daily equity for traders: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private hasDataChanged(newData: any[]): boolean {
return JSON.stringify(this.lastFetchedData) !== JSON.stringify(newData);
}
}
Preview:
downloadDownload PNG
downloadDownload JPEG
downloadDownload SVG
Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!
Click to optimize width for Twitter