Skip to content

cyberboyanmol/Finmo-backend-Assignment

Repository files navigation

Forex-trading-system Assignment

This is Implementation of Forex Trading backend system using Nestjs.

Table of Contents

  1. Forex-trading-system Assignment
  1. Installation Guide
  1. API Reference
  1. Support

Key Features:

  • Live FX Exchange Rates: Stay ahead of the market with live foreign exchange rates that are updated every 30 seconds, ensuring you have access to the most up-to-date pricing information for informed trading decisions.

  • Secure Authentication and Authorization: Robust authentication and authorization mechanisms are in place, employing industry-standard access and refresh token protocols. This ensures that your trading activities and sensitive data remain secure and accessible only to authorized users.

  • User Wallet Management: Users can easily topup their wallet account and check the balances in nearly all currencies.

  • Real-Time Currency Conversions: Leveraging the live exchange rates, users can convert amounts between different currencies with precision and accuracy.

Installation Guide

1. Prerequisites

  • Node.js >=v20.10.0 and Docker: Install the latest versions from their respective official websites: Nodejs and Docker.

2. Project Setup:

  • Clone the project repository using Git.
  • Navigate to the project directory using the cd command in your terminal.
  • Create a file named .env in the project's root directory.
  • Copy and paste the contents of the .env.example file into the newly created .env file. Replace the placeholder values with your own credentials (e.g., database connection details, API keys).

3. Install Dependencies:

Run the following command in your terminal to install the project's dependencies:

  npm install

4. Run the PostgreSQL and Redis Server using Docker (optional):

Run the following command to apply database migrations, ensuring your database schema is up-to-date:

docker-compose -f docker-compose.dev.yaml up -d

5. Generate Prisma Client:

Run the following command to generate the Prisma client, which facilitates interaction with your database:

  npm run prisma:generate

6. Apply Database Migrations:

Run the following command to apply database migrations, ensuring your database schema is up-to-date:

  npm run prisma:migrate:dev

6. Run the Project:

  • Run the Project (In one command)
  npm run dev

Run each apps separately (Optional):

  • Run Workers
  npm run workers
  • Run Cron
  npm run cron
  • Run Backend
  npm run backend

⚠️ Important Note:

This project utilizes the Alphavantage.co API (s://www.alphavantage.co/) for retrieving live exchange rates. However, the free tier has a strict rate limit of 25 requests per day.

Currently:

The project is configured using the demo API key, fetching only the USD to JPY exchange rate every 30 seconds.

To Unlock Full Currency Support:

You'll need a premium API key from Alphavantage.co, granting access to exchange rates for all currencies and a higher request limit (i.e 300+ request per minute).

How to Enable Full Currency Support (if you have a premium API key):

Code Updates (Backend App):

  • In the forex.service.ts file, uncomment the following two functions
  async fxConversion(body: FxConversionDto) {
    const {
      forex_exchange_rates_id,
      from_currency_code,
      to_currency_code,
      amount,
    } = body;
    const redisData = await this._redisService.getForexExchangeRate(
      ForexExchangeRatesLastestRedisKey
    );
    const dbData = forex_exchange_rates_id
      ? await this._forexExchangeRatesService.getForexExchangeRatesById({
          forex_exchange_rates_id,
        })
      : (
          await this._forexExchangeRatesService.getLatestForexExchangeRates()
        )[0];

    const exchangeRates = this.getExchangeRates(
      redisData,
      dbData,
      from_currency_code,
      to_currency_code
    );

    if (!exchangeRates) {
      throw new Error('Exchange rates not found');
    }

    const { fromCurrencyExchangeRate, toCurrencyExchangeRate } = exchangeRates;
    const result = CurrencyConverterService.convert({
      fromCurrencyCode: from_currency_code,
      fromCurrencyExchangeRateFromBaseCurrency:
        fromCurrencyExchangeRate.exchange_rate,
      toCurrencyCode: to_currency_code,
      toCurrencyExchangeRateFromBaseCurrency:
        toCurrencyExchangeRate.exchange_rate,
      amount,
    });

    return {
      convertedAmount: result,
      currency: to_currency_code,
    };
  }
 private getExchangeRates(
    redisData: ForexExchangeRatesRedisData | null,
    dbData: ForexExchangeRatesDbData | null,
    fromCurrencyCode: CurrencyCode,
    toCurrencyCode: CurrencyCode
  ): {
    fromCurrencyExchangeRate: CurrencyExchangeRate;
    toCurrencyExchangeRate: CurrencyExchangeRate;
  } | null {
    const data =
      redisData?.currency_exchange_rates ||
      dbData?.currency_exchange_rates ||
      [];

    const fromCurrencyExchangeRate = data.find(
      (rate) => rate.to_currency_code === fromCurrencyCode
    );
    const toCurrencyExchangeRate = data.find(
      (rate) => rate.to_currency_code === toCurrencyCode
    );

    if (!fromCurrencyExchangeRate || !toCurrencyExchangeRate) {
      return null;
    }

    return { fromCurrencyExchangeRate, toCurrencyExchangeRate };
  }
  • Code Modification Forex.controller.ts
const res = await this._forexService.fxConversionFormBaseCurrency(body);

to

const res = await this._forexService.fxConversion(body);

Code Updates Cron Job (Cron App):

Uncomment the cron scheduler in the file to enable fetching of exchange rates for all currencies every 30 seconds (recommended only if you have a premium API key).

@Cron(CronExpression.EVERY_30_SECONDS)
  async syncForexRatesEvery30Seconds() {
    const forex_exchange_rates_expires_at_milliseconds = 1000 * 30;
    const forex_exchange_rates_id = uuidV4();
    const forex_exchange_rates_expires_at = generateTimestamp(
      forex_exchange_rates_expires_at_milliseconds
    );
    currencyCodesWithName.map(async (currency: CurrencyCodeInterface) => {
      const params: ForexExchangeRateUrlProps = {
        to_currency: currency.code,
        apiKey: this._configService.ALPHA_VANTAGE_API_KEYS,
      };
      const url = getForexExchangeRateUrl(params);

      const eventData: SyncForexExchangeRateJob['data'] = {
        url,
        forex_exchange_rates_id,
        forex_exchange_rates_expires_at,
      };

      console.log('adding job :', forex_exchange_rates_id);
      this._forexExchangeRatesQueue.add(
        ForexJobPattern.SYNC_FOREX_EXCHANGE_RATES,
        { ...eventData }
      );
    });
  }

and comment :

  // THIS CRON WILL ONLY ADD THE USD TO INR FETCHING URL
  // DUE TO ALPHA VANTAGE API HARD RATE LIMIT (i.e 25 requests a day only)
  @Cron(CronExpression.EVERY_30_SECONDS)
  async syncForexExchangeRatesEvery30SecondsWithDemoKey() {
    const forex_exchange_rates_expires_at_milliseconds = 1000 * 30;
    const forex_exchange_rates_id = uuidV4();
    const forex_exchange_rates_expires_at = generateTimestamp(
      forex_exchange_rates_expires_at_milliseconds
    );
    const params: ForexExchangeRateUrlProps = {
      to_currency: 'JPY',
      apiKey: this._configService.ALPHA_VANTAGE_API_KEYS,
    };

    const url = getForexExchangeRateUrl(params);

    const eventData: SyncForexExchangeRateJob['data'] = {
      url,
      forex_exchange_rates_id,
      forex_exchange_rates_expires_at,
    };

    this._forexExchangeRatesQueue.add(
      ForexJobPattern.SYNC_FOREX_EXCHANGE_RATES,
      { ...eventData }
    );
    console.log('Adding jobs to queue :', forex_exchange_rates_id);
  }

API Reference

Base Url

  http://localhost:9000/

Swagger Docs

  http://localhost:9000/api/v1/docs
https://forex-trading-system-assignment-production.up.railway.app/api/v1/docs

Authentication

Register User

  POST /api/v1/auth/register
Parameter Type Description
email string Required.
password string Required.
firstName string Required.
lastName string Required. (Optional)

Login User

  POST /api/v1/auth/login
Parameter Type Description
email string Required.
password string Required.

Refresh Token

  GET /api/v1/auth/refresh-token

πŸ”’ NOTE: This Api Endpoint required Authorization in cookie need to be pass refresh_token".

Cookie: refresh_token=<your_refresh_token>

User Wallet

1. This API endpoint allows users to top up their account with a specified amount in a given currency.

πŸ“ NOTE: Currently, this API endpoint only supports the JPY currency.

πŸ”’ NOTE: This Api Endpoint required Authorization header "Bearer token".

  POST /api/v1/accounts/topup
Parameter Type Description
currency string Required.
amount string Required.

2. This API retrieves the balances in all currencies for the user's account..

πŸ“ NOTE: Currently, this API endpoint only supports the JPY currency.

πŸ”’ NOTE: This Api Endpoint required Authorization header "Bearer token".

  GET /api/v1/accounts/balance

Forex Endpoint

1.This API performs an FX conversion using the provided forex_exchange_rates_id and converts the specified amount from one currency to another.

πŸ“ NOTE: Currently, this endpoint is only working for the USD->JPY currency conversion.

  POST /api/v1/fx-conversion
Parameter Type Description
forex_exchange_rates_id string Required.
from_currency_code string Required.
to_currency_code string Required.
amount string Required.

2.This API endpoint gives a list of live FX exchange rates for all currencies.

πŸ“ NOTE: Currently, this endpoint is only working for the USD->JPY currency conversion.

  GET /api/v1/fx-rates

Support 🀝

If you encounter any issues or have questions while setting up this application, please don't hesitate to reach out! We're here to help!

Contact:

  • Email: anmolgangwar64+forex@gmail.com

Happy Hacking! πŸš€

About

Forex-Trading-System-Assignment

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published