DEV Community

Cover image for Having fun with money
Daniel Fitzpatrick
Daniel Fitzpatrick

Posted on

Having fun with money

I reached a significant financial milestone this month and am focusing on new goals. Like solving a performance problem, you should always measure first.

After poking around at different account aggregation tools, I settled on pocketsmith. Their simple REST API was a main draw:

That task alone wasn't interesting enough, so I wrote a small Clojure library to talk to the API. Ideally, it would leverage something like martian, but my attempt was unsuccessful. That could be a task for the future.

GitHub logo crinklywrappr / pocketsmith-api

small wrapper around the pocketsmith api for clojure developers

com.github.crinklywrappr/pocketsmith-api

Clojure library for interacting with the Pocketsmith REST API. Small, opinionated, and hand-crafted.

Coordinates

com.github.crinklywrappr/pocketsmith-api {:mvn/version "1.0.37"}
Enter fullscreen mode Exit fullscreen mode

Usage

First, require the library and define your user key. You can generate a key on the dashboard from your pocketsmith account.

(require '[crinklywrappr.pocketsmith-api.core :as ps])
(def mykey "xxx")
Enter fullscreen mode Exit fullscreen mode

Then, begin querying.

(def myuser (ps/authorized-user mykey :convert? true :minify? true))
;=> {:name "John Doe",
     :login "my-username"
     :email "my-address@email.com",
     :week-start-day 0,
     :id 1111111,
     :base-currency-code #object[org.joda.money.CurrencyUnit 0x4d1b5405 "USD"],
     :time-zone #object[org.joda.time.tz.CachedDateTimeZone 0x468ce331 "America/Chicago"]}

(def mycategories (into [] (ps/categories mykey myuser :flatten? true :normalize? true :convert? true :minify? true)))
;=> [{:id XXX, :title "Charity", :parents []}
     {:id XXX, :title "Taxes", :parents []}
     {:id XXX, :title "Bank", 
Enter fullscreen mode Exit fullscreen mode

pocketsmith-api uses Joda Time & Joda Money and has 99% code coverage. Because it deals with money, I wanted to ensure it read amounts accurately in various currencies.

That said, Joda Money has some limitations. I have listed some on the README.

Let's build something with it!

Acorns...?

Acorns is a savings tool that, in the past, worked by sending users monthly reports of how much they could save if they rounded up all their charges to the nearest dollar and transferred the difference to a savings account.

They may do more now.

I always thought that idea was excellent but I wanted to avoid paying for it. With Pocketsmith, we can accomplish that easily.

Here is the code.

Call (acorns) to get the desired amount.

Project setup

I prefer deps-new these days.

clojure -Tnew lib :name <project-name>
Enter fullscreen mode Exit fullscreen mode

Add crinklywrappr/pocketsmith-api to your deps.edn. Clojure will pull in all of the other dependencies you need transitively.

com.github.crinklywrappr/pocketsmith-api {:mvn/version "1.0.37"}
Enter fullscreen mode Exit fullscreen mode

Sanity checks

I like to be extra careful when dealing with money. Please verify some elementary assertions before borrowing this code for any purpose. I will demonstrate some tests on my account and list additional applicable checks.

The Clojure repl is unmatched for this.

Am I looking at the correct accounts? ✓

I only want to consider credit card accounts.

(into [] (r/map :name (cards (user))))
;=>
["Amex card" "Food card" "Amazon card" "Gas card" "Points card"]
Enter fullscreen mode Exit fullscreen mode

Am I requesting the correct transactions? ✓

Specifically, we only want to look at last month's debit transactions. The current date is June 19, 2023.

(last-month-query (user))
;=>
{:start_date "2023-05-01", :end_date "2023-05-31", :type "debit", :per_page 100}
Enter fullscreen mode Exit fullscreen mode

Am I receiving the correct transactions? ✓

Easy. We can sort the transactions by date and inspect the first and last elements.

(let [me (user)]
  (->> (last-month-charges me (cards me))
       (sort-by :date clj-time.core/before?)
       ((juxt first last))
       (mapv #(select-keys % [:date :payee]))))
;=>
[{:date
  #object[org.joda.time.LocalDateTime 0x5df45749 "2023-05-01T00:00:00.000"],
  :payee "SPOTIFY NEW YORK NY P22C7B5A33 XXXXXX1161"}
 {:date
  #object[org.joda.time.LocalDateTime 0x140ae942 "2023-05-31T00:00:00.000"],
  :payee "DOORDASH*LUNCH BOX CSAN FRANCIS NT_NZXE7CMB +XXXXXXX9470"}]
Enter fullscreen mode Exit fullscreen mode

Does the math make sense? ✓

We can do this by taking 10 of the transactions and eyeballing the numbers.

(require '[clojurewerkz.money.format :as mf])
(let [me (user)]
  (clojure.pprint/print-table
   (->> (last-month-charges me (cards me))
        (map
         (fn [x]
           {:rounded (mf/format (round-up x))
            :amount (mf/format (:amount x))
            :difference (mf/format (difference x))}))
        (take 10))))

| :rounded | :amount | :difference |
|----------+---------+-------------|
|  $-55.00 | $-54.80 |       $0.20 |
|  $-26.00 | $-25.44 |       $0.56 |
|  $-29.00 | $-28.02 |       $0.98 |
|  $-23.00 | $-22.19 |       $0.81 |
|   $-3.00 |  $-2.99 |       $0.01 |
|  $-38.00 | $-37.94 |       $0.06 |
|  $-50.00 | $-49.81 |       $0.19 |
|  $-16.00 | $-15.44 |       $0.56 |
|  $-24.00 | $-23.90 |       $0.10 |
|   $-1.00 |  $-0.64 |       $0.36 |

Enter fullscreen mode Exit fullscreen mode

$54.80 rounds up to $55.00, and the difference is $0.20, and so forth.

Additional checks

There are other assertions you may want to test, and I encourage you to do so.

  • Are all the differences under $1.00?
  • Does difference respond with $0.00 when given an even dollar charge?
  • Is the total amount less than the number of charges?
  • Are all the charges truly debits (negative dollar amounts)?

Conclusion

This project was a fun distraction while gearing up for my next financial goals. Hopefully, this selfish exercise will help someone else. 😉

By the way, I need to squirrel away $40.63 from last month.

Top comments (0)