Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Task 4 — Exchange Office Network

Objective

Create a model for an exchange-office network.

The system contains two exchange markets with different exchange rates.

Users submit transactions that convert money from one currency into another.

For every transaction, the implementation must:

  • determine the requested currency pair
  • compare exchange rates between markets
  • select the better exchange market
  • calculate the converted amount
  • identify the user account
  • credit the converted amount to the destination-currency sector
  • produce a structured exchange result
  • produce logs
  • print output in JSON format

Supported Currencies

The system supports:

GBP — UK Pound
USD — US Dollar
EUR — Euro
RUB — Russian Ruble
JPY — Japanese Yen
INR — Indian Rupee

A typed currency representation is recommended.

For example:

type Currency string

const (
    CurrencyGBP Currency = "GBP"
    CurrencyUSD Currency = "USD"
    CurrencyEUR Currency = "EUR"
    CurrencyRUB Currency = "RUB"
    CurrencyJPY Currency = "JPY"
    CurrencyINR Currency = "INR"
)

Exchange Market

Each exchange market contains rates for supported currency transformations.

A possible model is:

type ExchangeMarket struct {
    ID    int
    Rates []ExchangeRate
}

with:

type ExchangeRate struct {
    From Currency
    To   Currency
    Rate float64
}

For example:

GBP -> USD = 1.189

means:

1 GBP = 1.189 USD

Exchange Market 1

Use the following rates:

GBP -> USD = 1.189
GBP -> EUR = 1.184
GBP -> RUB = 77.294
GBP -> JPY = 163.314
GBP -> INR = 94.480

USD -> GBP = 0.840
USD -> EUR = 0.995
USD -> RUB = 65.000
USD -> JPY = 137.265
USD -> INR = 79.435

EUR -> GBP = 0.844
EUR -> USD = 1.004
EUR -> RUB = 65.279
EUR -> JPY = 137.790
EUR -> INR = 79.768

RUB -> GBP = 0.012
RUB -> USD = 0.015
RUB -> EUR = 0.015
RUB -> JPY = 2.110
RUB -> INR = 1.222

JPY -> GBP = 0.006
JPY -> USD = 0.007
JPY -> EUR = 0.007
JPY -> RUB = 0.473
JPY -> INR = 0.578

INR -> GBP = 0.010
INR -> USD = 0.012
INR -> EUR = 0.012
INR -> RUB = 0.818
INR -> JPY = 1.727

Exchange Market 2

Use the following rates:

GBP -> USD = 1.180
GBP -> EUR = 1.189
GBP -> RUB = 76.294
GBP -> JPY = 164.314
GBP -> INR = 93.480

USD -> GBP = 0.860
USD -> EUR = 0.999
USD -> RUB = 64.000
USD -> JPY = 136.265
USD -> INR = 80.435

EUR -> GBP = 0.824
EUR -> USD = 1.000
EUR -> RUB = 66.279
EUR -> JPY = 138.790
EUR -> INR = 80.768

RUB -> GBP = 0.082
RUB -> USD = 0.010
RUB -> EUR = 0.018
RUB -> JPY = 2.010
RUB -> INR = 1.122

JPY -> GBP = 0.004
JPY -> USD = 0.009
JPY -> EUR = 0.010
JPY -> RUB = 0.495
JPY -> INR = 0.555

INR -> GBP = 0.012
INR -> USD = 0.010
INR -> EUR = 0.011
INR -> RUB = 0.805
INR -> JPY = 1.740

Exchange Rate Data Source

The exchange-rate tables must first be transformed into JSON.

The application must then load the exchange-market data from that JSON file.

A possible JSON structure is:

[
  {
    "marketID": 1,
    "rates": [
      {
        "from": "GBP",
        "to": "USD",
        "rate": 1.189
      }
    ]
  }
]

The exact JSON structure is left to the implementation.

The important requirement is that the rates are loaded from a file rather than hard-coded directly into processing logic.

Selecting the Best Exchange Market

For a transaction:

GBP -> USD

compare the GBP-to-USD rate from every market.

Market 1:

1.189

Market 2:

1.180

For the same source amount, the larger rate produces more destination currency.

Therefore:

Market 1

is the better market for this transformation.

For:

GBP -> EUR

the rates are:

Market 1 = 1.184
Market 2 = 1.189

Therefore:

Market 2

is better.

User Account

Each account contains:

Username
Account ID
Money stored in six currency sectors

The currency-sector mapping is:

Sector 0 -> USD
Sector 1 -> GBP
Sector 2 -> EUR
Sector 3 -> RUB
Sector 4 -> JPY
Sector 5 -> INR

A possible model is:

type Account struct {
    Username  string
    AccountID uint64
    Balances  map[Currency]float64
}

A typed currency map is preferable to accessing sectors using unexplained integer indexes throughout the application.

Account Data

Use the following accounts:

Alex   |2453425652|50000   |60000   |70000    |5000000|6000000|8000000
Ben    |8674652543|60000   |70000   |80000    |6000000|7000000|10000000
David  |8562953754|30000   |40000   |30000    |3000000|4000000|20000000
Emma   |848563421 |10000   |10000   |10000    |1000000|1000000|10000000
Fiona  |335647689 |5000    |5000    |5000     |100000 |100000 |1000000
Gina   |242564578 |4000    |4000    |4000     |50000  |50000  |500000
Alex   |667783546 |0       |0       |0        |0      |0      |0
Tina   |565283951 |0       |0       |0        |0      |0      |0
Simon  |4677859941|500000  |600000  |700000   |0      |0      |0
Selena |4877639720|250000  |350000  |450000   |0      |0      |0
Nolan  |7445668990|0       |0       |0        |2500000|4500000|7500000
Naomi  |352678541 |0       |0       |0        |7500000|8500000|3500000
Paul   |9122387545|10000000|10000000|10000000 |0      |0      |0
Lana   |8766234490|750000  |2000000 |10000000 |0      |0      |0
Nolan  |2556779340|0       |2500000 |2500000  |0      |0      |0

The balance column order is:

USD
GBP
EUR
RUB
JPY
INR

Transaction Model

Each transaction contains:

Source Currency
Destination Currency
Amount

A possible model is:

type Transaction struct {
    From   Currency
    To     Currency
    Amount float64
}

A user’s transaction request also needs account identity.

For example:

type TransactionRequest struct {
    RequestID string
    Username  string
    AccountID uint64
    Transaction Transaction
}

Transaction List

Use the following requests:

Alex  |2453425652|GBP-USD-500000
Ben   |8674652543|GBP-EUR-750000
David |8562953754|USD-EUR-800000
Ema   |848563421 |USD-JPY-900000
Fiona |335647689 |EUR-RUB-1000000
Gina  |242564578 |EUR-INR-1200000
Alex  |667783546 |RUB-GBP-15000000
Tina  |565283951 |RUB-USD-17500000
Simon |4677859941|JPY-GBP-25000000
Selena|4877639720|JPY-EUR-30000000
Nolan |7445668990|INR-RUB-5000000
Naomi |352678541 |INR-JPY-8000000
Paul  |9122387545|USD-EUR-650000|GBP-EUR-850000
Lana  |8766234490|RUB-GBP-16500000|RUB-USD-19500000
Nolan |2556779340|JPY-GBP-32000000|JPY-EUR-36500000

Some accounts contain more than one transaction.

The implementation must process every listed transaction.

Source Data Inconsistency

The account table contains:

Emma | 848563421

while the transaction table contains:

Ema | 848563421

The Account ID is the same.

The implementation should not silently assume that arbitrary username differences are equivalent.

A reasonable solution is to use:

AccountID

as the primary account identifier and report the username mismatch as a data inconsistency.

Exchange Calculation

For:

amount = A
rate   = R

the converted amount is:

converted = A * R

For example:

500000 GBP
GBP -> USD
best rate = 1.189

produces:

500000 * 1.189 = 594500 USD

Destination Sector Update

After conversion, the converted amount must be sent to the account sector corresponding to the destination currency.

For:

GBP -> USD

the resulting money belongs to:

USD
Sector 0

For:

RUB -> GBP

the result belongs to:

GBP
Sector 1

Important Source Limitation

The source explicitly states that the converted amount must be sent to the destination account sector.

It does not explicitly define:

  • whether the original source amount must be debited
  • whether insufficient source balance should reject a transaction
  • whether overdrafts are allowed

Several supplied transaction amounts are also larger than the corresponding example account balances.

Therefore the base task should not silently invent insufficient-funds behavior.

If source-balance deduction is implemented, it should be clearly documented as an extension to the original task.

Exchange Query Result

Each processed exchange query must contain:

Query ID
Request Person
Source Currency
Destination Currency
Original Amount
Selected Market
Best Exchange Rate
Converted Amount
Destination Sector

A possible model is:

type ExchangeResult struct {
    RequestID       string
    Username        string
    AccountID       uint64
    From            Currency
    To              Currency
    SourceAmount    float64
    MarketID        int
    ExchangeRate    float64
    ConvertedAmount float64
    DestinationSector int
}

Multiple Transactions

Accounts such as:

Paul
Lana
Nolan

contain multiple exchange operations.

Every transaction must be independently evaluated because different currency pairs may select different exchange markets.

Equal Exchange Rates

The source does not define what should happen when both markets have the same rate.

The implementation must define a deterministic rule.

For example:

select the market with the lowest Market ID

This rule should be documented.

Logging

The implementation must create logs for exchange processing.

Useful log information includes:

request ID
account ID
currency pair
selected market
exchange rate
converted amount
processing result

The exact logging framework is left to the implementation.

JSON Output

The final exchange result must be printable in JSON format.

Example:

{
  "requestID": "exchange-1",
  "username": "Alex",
  "accountID": 2453425652,
  "from": "GBP",
  "to": "USD",
  "sourceAmount": 500000,
  "marketID": 1,
  "exchangeRate": 1.189,
  "convertedAmount": 594500,
  "destinationSector": 0
}

Validation

The implementation should detect:

unknown account
unsupported currency
invalid currency pair
missing exchange rate
invalid transaction amount
invalid market data
malformed transaction input

Transaction amounts should be greater than zero.

A transformation where:

From == To

should be rejected because no exchange operation is required.

Requirements

The implementation must:

  1. model supported currencies
  2. model exchange markets
  3. model exchange rates
  4. transform the supplied exchange-rate tables into JSON
  5. load exchange rates from JSON
  6. model user accounts
  7. preserve the defined currency-sector mapping
  8. model transaction requests
  9. parse all supplied transactions
  10. compare exchange markets
  11. select the best exchange rate
  12. calculate the converted amount
  13. credit the destination currency sector
  14. return exchange-query information
  15. implement processing logs
  16. print results in JSON format

Modeling Goal

The task combines several independent domain concepts:

ExchangeMarket
      |
      +-- ExchangeRate

Account
      |
      +-- Currency Balances

TransactionRequest
      |
      +-- Account
      +-- Currency Pair
      +-- Amount
      |
      v
Exchange Processor
      |
      +-- Select Market
      +-- Convert
      +-- Update Destination Sector
      +-- Log
      +-- JSON Result

The implementation should keep exchange-rate data, account data, transaction input, and processing logic separated rather than placing all behavior into one large structure.

Scalionix Docs

Keyboard Shortcuts

Navigate the documentation without leaving the keyboard.
Navigation
Previous subject
←
Next subject
→
Previous subsection
Alt + ↑
Next subsection
Alt + ↓
Interface
Documentation Home
Ctrl + Enter
Search
Alt + Q
Open shortcuts
?
Close dialog
Esc
Scalionix Docs

Search Documentation