DEV Community

Cover image for Programming financial transactions
Stefan Willebrand
Stefan Willebrand

Posted on

Programming financial transactions

One of the simplest yet most complex and important element of finance is the transaction! When building financial software it has always been a struggle in each project until we have been able to nail down the transaction engine.

If you go too shallow when building this thing it will come back and bite you since you will be limited with future functionality. Building it deep enough when it comes to granularity takes a lot of time and testing. You want to make sure that the solution is configurable and dynamic enough to not fall over when you introduce new use cases.

In my earlier years as a FinTech developer focusing on wealth management solutions this is where my hair started to become thinner 😁.

I wanted to do this once and for all and never have to care about it again ever in my life so Bricknode came to be!

This article is not about explaining all the secret details how we got this fantastic transactional engine to work, some of the secrets are shared here, but rather spread the knowledge about how to use it.

The learning curve is not that steep and once you grasp this you will see how you can build any type of financial application without inventing the wheel again.

To make it even simpler I have decided to share a code project in GitHub which you can use as much as you want to play around with this. It is called Bricknode Broker Utilities and I will add more and more interesting functionality to illustrate.

Bricknode Broker Utilities

Except for the really cool graphics this application will illustrate how to work with the API and I have inputted my instance of Bricknode Broker into the user secrets.

In the image below I have successfully logged on to this application and the menu with the available functions presents itself.

Awesome graphics!

The first function is for creating a transaction and the code for the transaction service looks like this:

using System.Globalization;
using BfsApi;

namespace Bricknode_Broker_Utilities.Services;

public class TransactionService
{
    private readonly BfsDataService _bfsDataService;

    public TransactionService(BfsDataService bfsDataService)
    {
        _bfsDataService = bfsDataService;
    }

    /// <summary>
    ///     This method will deposit cash into an account within the Bricknode Broker instance
    /// </summary>
    /// <returns></returns>
    public async Task DepositCash(string bfsInstanceKey)
    {
        Console.WriteLine("Account number:");
        var accountNumber = int.Parse(Console.ReadLine() ?? string.Empty);

        var account = await _bfsDataService.GetAccountByNumber(accountNumber, bfsInstanceKey);

        Console.WriteLine("Amount to deposit:");
        var amount = decimal.Parse(Console.ReadLine() ?? string.Empty);

        Console.WriteLine("The following cash assets are available to deposit:");

        var cashAssets = await _bfsDataService.GetAvailableCashAssets(bfsInstanceKey);

        foreach (var cashAsset in cashAssets) Console.WriteLine($"{cashAsset.Key}");

        Console.WriteLine("");

        Console.WriteLine("Enter the three letter symbol for the cash asset that you would like to deposit:");

        var cashAssetKey = Console.ReadLine();

        var selectedCashAsset = cashAssets.Single(t => t.Key == cashAssetKey);

        Console.WriteLine("Enter trade date in the following format (YYYY-MM-DD)");
        var tradeDate =
            DateTime.ParseExact(Console.ReadLine() ?? string.Empty, "yyyy-MM-dd", CultureInfo.InvariantCulture);

        Console.WriteLine("Enter settle date in the following format (YYYY-MM-DD)");
        var settleDate =
            DateTime.ParseExact(Console.ReadLine() ?? string.Empty, "yyyy-MM-dd", CultureInfo.InvariantCulture);

        var superTransaction = CreateSuperTransaction(account, amount, selectedCashAsset, tradeDate, settleDate);

        var response = await _bfsDataService.CreateTransaction(superTransaction, bfsInstanceKey);

        if (response.Message == "OK")
        {
            Console.WriteLine("Success! The transaction was created.");
            return;
        }

        Console.WriteLine($"No transaction was created because of the following error: {response.Message}");
    }

    private SuperTransaction CreateSuperTransaction(GetAccountResponseRow account, decimal amount,
        GetCashResponseRow selectedCashAsset, DateTime tradeDate, DateTime settleDate)
    {
        return new SuperTransaction
        {
            Batch = Guid.NewGuid(),
            BusinessEventId = Guid.NewGuid(),
            BusinessTransactions = new[]
            {
                new BusinessTransaction
                {
                    Account = account.BrickId,
                    AmountAsset1 = amount,
                    Asset1 = selectedCashAsset.BrickId,
                    BusinessTransactionType = "Default_Transfer_Trade_Cash",
                    TradeDate = tradeDate
                },
                new BusinessTransaction
                {
                    Account = account.BrickId,
                    AmountAsset1 = amount,
                    Asset1 = selectedCashAsset.BrickId,
                    BusinessTransactionType = "Default_Transfer_Settle_Cash",
                    SettlementDate = settleDate,
                    ValueDate = settleDate
                }
            }
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

A “transaction”, as it is experienced by a bank or brokerage customer looking at their account is actually made up of a hierarchy of transaction levels to make this work in reality.

The “customer facing” transaction, or the top level of the hierarchy so to speak is called a Super Transaction in Bricknode Broker and you need to care about this one and its first child called Business Transaction.

The API methods are described here.

A Super Transaction should always be made up of two Business Transactions where the Business Transactions influences what we call different dimensions.

Imagine a bucket which is being filled or emptied depending on if money is pouring in or out.

The bucket concept

In our world the bucket is being housed by an account and an account can house and keep track of several buckets at a time.

The Dimension is a label that we can put on a bucket, instead of one bucket holding water and another bucket holding milk we can say that a bucket holds cash in the Trade dimension and another bucket holds cash in the Settle dimension.

Multiple buckets

We can then add even more dimensions like Accrued Interest if we wish and the buckets are simply holding a balance for assets in a certain dimension.

Let’s focus on the part of the code where the Super Transaction is created.

private SuperTransaction CreateSuperTransaction(GetAccountResponseRow account, decimal amount,
        GetCashResponseRow selectedCashAsset, DateTime tradeDate, DateTime settleDate)
    {
        return new SuperTransaction
        {
            Batch = Guid.NewGuid(),
            BusinessEventId = Guid.NewGuid(),
            BusinessTransactions = new[]
            {
                new BusinessTransaction
                {
                    Account = account.BrickId,
                    AmountAsset1 = amount,
                    Asset1 = selectedCashAsset.BrickId,
                    BusinessTransactionType = "Default_Transfer_Trade_Cash",
                    TradeDate = tradeDate
                },
                new BusinessTransaction
                {
                    Account = account.BrickId,
                    AmountAsset1 = amount,
                    Asset1 = selectedCashAsset.BrickId,
                    BusinessTransactionType = "Default_Transfer_Settle_Cash",
                    SettlementDate = settleDate,
                    ValueDate = settleDate
                }
            }
        };
    }
Enter fullscreen mode Exit fullscreen mode

Where the Business Transactions are created there is a property called BusinessTransactionType being set. This is very important because the definition of the transaction type holds information about which Dimension the corresponding transactions will be tagged to.

Now, how on earth can you figure out what transaction type to use as the input?

Well, this is where things get a bit tricky and Bricknode, or some developer 😉, should create some tooling around this! Maybe an idea for one of you that you can add to the Bricknode Marketplace! 💡

By navigating to Transaction Types in System Data you can filter on types:

Bricknode Broker Admin

The naming convention of the transaction types tells you a lot. The first section starts usually with Default and means that these have been created by default by the system. The second section can be Trade, Transfer or Payment and that tells you the master type group of the transaction and reflects if it impacts the return of the account or not. A Trade and Payment can impact the return of the account but not a Transfer.

The third section tells if the transaction type will generate an entry in the Trade or Settle dimension and the sections that follow are simply more granular details of what the transaction type is used for.

To double check what will happen when the transaction type is used it is possible to download the actual transaction type mapping as it is called. This can be done by checking the box in front of a transaction type and then click the Export button.

For the transaction type called Default_Transfer_Trade_Cash the mapping looks like this (XML).

<BusinessTransactionMappingRules xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.bricknode.com/TransactionMapping">
    <BusinessTransactionMappingRule transactionType="Default_Transfer_Trade_Cash">
        <AccountTransactionItems>
            <AccountTransaction dimension="T" assetType="Asset1" amountType="AmountAsset1" factor="1" transactionDate="TradeDate" xsi:type="DefaultSystemAccount" useAcquisitionValue="false"/>
        </AccountTransactionItems>
    </BusinessTransactionMappingRule>
</BusinessTransactionMappingRules>
Enter fullscreen mode Exit fullscreen mode

In this example where it says dimension=”T” it means Trade as the dimension, if it says S it means Settle. The dimensions used can be seen in the GUI section called Dimensions where new dimensions can be entered ad-hoc according to needs.

Dimensions

I have used many financial applications which have had a go at creating the ultimate transaction engine but IMHO nothing comes close to what has been created by Bricknode.

I am yet to see a use case that this model could not cater to and if it would be possible to get a patent for a transaction engine this should be it 🙂.

Please get in touch if you have any questions, ideas or requests that you would like me to address in the future and code some examples of to illustrate.

Top comments (0)