Trendwatch Taxable Gains

posted in: Tech Journal | 0

Introduction

When I realized that I didn’t understand how the taxable capital gains were being calculated on my T5008 statements, I decided to build an app to help me automate my data entry, accounting, and reporting workflows.

Laravel may or may not be the best tool for this job but it’s my strongest hammer and this problem looks a lot like a big nail.

My goal is to build each level of functionality in an incremental manner.

TDD

While watching a YouTube video by Dave Farley on the Continuous Delivery channel, something clicked for me in terms of how I had been approaching TDD. I thought that because I wrote some tests before writing code, I was practicing TDD. I was practicing TDD (emphasis on practicing), but only up to a point. I eventually reached a stage where my code ended up validating the tests, instead of tests validating the code. I mean, the code was working! The code was correctly calculating results based on the inputs that were given. It’s the test expectations that were incorrect!

If our tests are difficult to write, our design is poor!

When Test Driven Development Goes Wrong, Continuous Delivery

The expectations were supposed to be based on a series of calculations on the test data. Calculations that I had correctly programmed into the business logic layer, but had incorrectly calculated when I hardcoded them into the test expectations. That’s why I was building this program anyway; to automate my manual procedures!

My skill at writing program logic was clearly stronger than my skill at writing TDD tests.

The thing that clicked for me when I watched Dave Farley’s video was that I was not allowing my tests to drive development. When things got difficult with the testing side, I went with my strong suit–I wrote code that worked and set TDD aside.

If I was going to succeed at TDD I would have to do more than just write tests first. I would have to commit to letting the tests drive the design.

Example – Buy And Sell Test

Having gone through the pain of calculating taxable gains myself for the first time instead of letting an accountant do it for me, I gained some valuable domain knowledge. Some pain, some gain.

I decided to start off with a simple scenario.

  • Buy 100 shares for $50.00/share, with commission at $12.49.
  • Sell 100 shares for $75.00/share, with commission at $9.99.
  • Expect that the cost of the trade would be calculated as $50.00 x 100 + $12.49 = $5,012.49.
  • Expect that the proceeds from disposition would be calculated as $75 x 100 – $9.99 = $7,490.01.

The calculated costs and proceeds are the numbers I could enter on a T5008 form. Note that these inputs are for test data only and do not necessarily reflect my real trades!

A domain driven approach

The test scenario I outlined above could reflect a real-life workflow in which I would

  • Issue a Buy order with my broker,
  • Download the confirmation slip for the purchase. Use this to determine the cost value.
  • Eventually issue a Sell order with my broker,
  • Download the confirmation slip for the disposition. Use this to determine the proceeds value.
  • The difference between cost and proceeds determine the capital gain or loss for reporting on my tax return.

I’m not going to pretend that I’m using DDD proper; however, I am going to let it be an influence on my design decisions.

To make my code read as closely as possible to an English statement of my workflow, I might want to write my test something like this:

        $buy_TQ001 = $this->buy_TQ001();
        $sell_TQ001 = $this->sell_TQ001();
    
        // Buy 100 shares TQQQ @ $50 + $12.49 commission (SecNum = TQ001)
        // Expect cost base is $5000 + $12.49
        $service->record($buy_TQ001);

        // Sell 100 shares TQQQ @ $75 + $9.99 commission (SecNum = TQ001)
        // Expect proceeds from disposition is $7500 - $9.99
        $service->record($sell_TQ001);

The buy_TQ001() and sell_TQ001() methods are helpers which set up data structures with the appropriate values. The record() method is a function in the business logic which “records” the relevant values for future use. I’m not crazy about the name record. I considered having separate service methods called buy() and sell() and decided to go with a single method instead. I’m sticking with the name record until I think of a better name.

With this test in place, I let it drive towards this implementation for the business logic:

    public function record(TradeConfirmationDTO $confirmation_dto)
    {
        $confirmation = $confirmation_dto->toArray();

        $action = $confirmation['action'];
        $security_number = $confirmation['security_number'];

        if ($action->isBuy())
        {
            $net_transaction_amount = GrossTransactionAmount::fromArray($confirmation)
                ->plusFees();
            $this->trade_summary_list[$security_number]['cost_base'] = $net_transaction_amount;
        }

        if ($action->isSell())
        {
            $net_transaction_amount = GrossTransactionAmount::fromArray($confirmation)
                ->minusFees();
            $this->trade_summary_list[$security_number]['proceeds'] = $net_transaction_amount;
        }
    }

This implementation supports the evaluation of costs and proceeds for a specified security through the elements of the trade_summary_list array.

I keep changing my mind about which terminology to go with: cost_base, cost, or costs. Other than that, I am reasonably satisfied with the readability of this implementation and clarity of intent. I think it’s much clearer than some of the earlier iterations I came up with along the way. Of course, as I continue to add features, I will probably evolve this code further.

Key learnings

  • Let the tests *drive* the design and development rather than be driven by them, especially when it gets difficult.
  • When the tests get difficult to write, that is an indication that the design is poor.
  • Test and build only one requirement/use case scenario.