Coding assessment (Scala 3, ZIO).
Completed in 2024, and currently maintained as a demonstration project.
The program reads the inputs from the orders.txt
and clients.txt
files and writes the output to the results.txt
file.
sbt run
- runsbt test
- testsbt styleFix
- fix formatting and linting errorssbt styleCheck
- check for formatting and linting errorssbt dev
- allow compiler warnings to not fail the compilation
ExchangeState
– responsible for processing ordersFileApi
– a file-based API for the exchange (loading client balances to the state and back, interacting with matcher). Also knows about the string syntax.Cli
– responsible for dealing with the file system and interacting with the user.
- All types and data structures are immutable.
OrderAmount
andAssetPrice
are forced to be positive (and never zero) using the opaque types.- This means that orders with zero amount or zero price are rejected not because they are invalid from the
ExchangeState
perspective, but from a parsing perspective. This means, that iforders.txt
contains an order with zero amount or zero price, the program will exit withExitCode.failure
. In a real-world scenario, we won't want to accept such orders from customers, and reject them without submitting, which also fits the current design choice. - The alternative could be to allow zero amounts and prices, but this would make the core logic less reliable.
- This means that orders with zero amount or zero price are rejected not because they are invalid from the
- The task assumes that there are only 4 available assets:
A
,B
,C
, andD
, but the parser doesn't restrict theAssetName
syntax to these 4. Instead, the invalid assetName will be rejected by theExchangeState.processOrder
with theOrderRejectionReason.UnknownAsset
error. - Similarly,
AssetAmount
andUsdAmount
(used for client balances and most computations) are forced to be non-negative. - Every customer balance is represented via 2 parts:
free
andlocked
. Functions that manage theOrderBook
are implemented in such a way that they automatically update locked and free balances every time the order is inserted or removed. This way, in any givenExchangeState
locked client balances are expected to be consistent with theOrderBook
.- This choice means that in order to work with the existing order from the book, it is removed from the queue, and then the remaining part could be put back, and the updated state doesn't contain such
temporarily removed
orders. - The
free
(not thetotal
) balance is used to check if the client has sufficient funds for the order. By locking the funds for the orders in the order book, we guarantee that once the order is placed, the client has enough funds to execute it.
- This choice means that in order to work with the existing order from the book, it is removed from the queue, and then the remaining part could be put back, and the updated state doesn't contain such
- Order price is a so-called "limit price". The order is executed at the best available price, but not worse than the limit price.
- A better price can be used if there are orders in the order book with a better price than specified in the order.
- After all matching orders are used, and the order is placed in the order book, only its specified limit price will be used for trades.
- All errors are represented as ADTs, and the program never throws exceptions, but uses error handling capabilities of
Either
,ZIO
, andZStream
instead. - There is a
UnexpectedInternalError
variant in theUnexpectedInternalError
ADT. It corresponds to errors that must never happen as long as the input data is correctly parsed (which it is expected to be).- Currently, such error variant doesn't specific error message, because there's nothing to explain to the user.
- In real-world scenario, such error could contain technical details for the developers. However, the decision is to leave it outside of the scope of this task.
- Loading initial balances into the
ExchangeState
is considered a part of theFileApi
, because it is a simplified file-based way to set the balances.- In a real-world scenario, we would prefer the core to work with the deposit and withdrawal operations instead of allowing the client code to simply set the balance.
- The sum of all client balances of USD and of every individual asset is the same before and after processing any orders (because there are no exchange fees).
- The sum of locked assets is equal to the sum of order amounts in the order book.