diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4876af5..e60bc9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
 # Changelog
 
+## Version 0.1.17
+
+### Added
+
+- transactions note
+
+### Fixed
+
+- coin statistics
+
 ## Version 0.1.16
 
 ### Fixed
diff --git a/app/actions/data.ts b/app/actions/data.ts
index 23e0c69..a170989 100644
--- a/app/actions/data.ts
+++ b/app/actions/data.ts
@@ -104,7 +104,8 @@ export async function addCoins(
   amount: number,
   description: string,
   type: TransactionType = 'MANUAL_ADJUSTMENT',
-  relatedItemId?: string
+  relatedItemId?: string,
+  note?: string
 ): Promise<CoinsData> {
   const data = await loadCoinsData()
   const newTransaction: CoinTransaction = {
@@ -113,7 +114,8 @@ export async function addCoins(
     type,
     description,
     timestamp: d2t({ dateTime: getNow({}) }),
-    ...(relatedItemId && { relatedItemId })
+    ...(relatedItemId && { relatedItemId }),
+    ...(note && note.trim() !== '' && { note })
   }
 
   const newData: CoinsData = {
@@ -144,7 +146,8 @@ export async function removeCoins(
   amount: number,
   description: string,
   type: TransactionType = 'MANUAL_ADJUSTMENT',
-  relatedItemId?: string
+  relatedItemId?: string,
+  note?: string
 ): Promise<CoinsData> {
   const data = await loadCoinsData()
   const newTransaction: CoinTransaction = {
@@ -153,7 +156,8 @@ export async function removeCoins(
     type,
     description,
     timestamp: d2t({ dateTime: getNow({}) }),
-    ...(relatedItemId && { relatedItemId })
+    ...(relatedItemId && { relatedItemId }),
+    ...(note && note.trim() !== '' && { note })
   }
 
   const newData: CoinsData = {
diff --git a/components/CoinsManager.tsx b/components/CoinsManager.tsx
index d5f8e25..60ed4a5 100644
--- a/components/CoinsManager.tsx
+++ b/components/CoinsManager.tsx
@@ -4,7 +4,7 @@ import { useState } from 'react'
 import { t2d, d2s, getNow, isSameDate } from '@/lib/utils'
 import { Button } from '@/components/ui/button'
 import { FormattedNumber } from '@/components/FormattedNumber'
-import { History } from 'lucide-react'
+import { History, Pencil } from 'lucide-react'
 import EmptyState from './EmptyState'
 import { Input } from '@/components/ui/input'
 import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
@@ -12,11 +12,13 @@ import { settingsAtom } from '@/lib/atoms'
 import Link from 'next/link'
 import { useAtom } from 'jotai'
 import { useCoins } from '@/hooks/useCoins'
+import { TransactionNoteEditor } from './TransactionNoteEditor'
 
 export default function CoinsManager() {
   const {
     add,
     remove,
+    updateNote,
     balance,
     transactions,
     coinsEarnedToday,
@@ -31,14 +33,26 @@ export default function CoinsManager() {
   const [pageSize, setPageSize] = useState(50)
   const [currentPage, setCurrentPage] = useState(1)
 
+  const [note, setNote] = useState('')
+
+  const handleSaveNote = async (transactionId: string, note: string) => {
+    await updateNote(transactionId, note)
+  }
+
+  const handleDeleteNote = async (transactionId: string) => {
+    await updateNote(transactionId, '')
+  }
+
   const handleAddRemoveCoins = async () => {
     const numAmount = Number(amount)
     if (numAmount > 0) {
-      await add(numAmount, "Manual addition")
+      await add(numAmount, "Manual addition", note)
       setAmount(DEFAULT_AMOUNT)
+      setNote('')
     } else if (numAmount < 0) {
-      await remove(Math.abs(numAmount), "Manual removal")
+      await remove(Math.abs(numAmount), "Manual removal", note)
       setAmount(DEFAULT_AMOUNT)
+      setNote('')
     }
   }
 
@@ -59,45 +73,51 @@ export default function CoinsManager() {
           </CardHeader>
           <CardContent>
             <div className="space-y-6">
-              <div className="flex items-center justify-center gap-4">
-                <Button
-                  variant="outline"
-                  size="icon"
-                  className="h-10 w-10 text-lg"
-                  onClick={() => setAmount(prev => (Number(prev) - 1).toString())}
-                >
-                  -
-                </Button>
-                <div className="relative w-32">
-                  <Input
-                    type="number"
-                    value={amount}
-                    onChange={(e) => setAmount(e.target.value)}
-                    className="text-center text-xl font-medium h-12"
-                  />
-                  <div className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground">
-                    🪙
+              <div className="flex flex-col gap-4">
+                <div className="flex items-center justify-center gap-4">
+                  <Button
+                    variant="outline"
+                    size="icon"
+                    className="h-10 w-10 text-lg"
+                    onClick={() => setAmount(prev => (Number(prev) - 1).toString())}
+                  >
+                    -
+                  </Button>
+                  <div className="relative w-32">
+                    <Input
+                      type="number"
+                      value={amount}
+                      onChange={(e) => setAmount(e.target.value)}
+                      className="text-center text-xl font-medium h-12"
+                    />
+                    <div className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground">
+                      🪙
+                    </div>
                   </div>
+                  <Button
+                    variant="outline"
+                    size="icon"
+                    className="h-10 w-10 text-lg"
+                    onClick={() => setAmount(prev => (Number(prev) + 1).toString())}
+                  >
+                    +
+                  </Button>
                 </div>
-                <Button
-                  variant="outline"
-                  size="icon"
-                  className="h-10 w-10 text-lg"
-                  onClick={() => setAmount(prev => (Number(prev) + 1).toString())}
-                >
-                  +
-                </Button>
-              </div>
 
-              <Button
-                onClick={handleAddRemoveCoins}
-                className="w-full h-14 transition-colors flex items-center justify-center font-medium"
-                variant="default"
-              >
-                <div className="flex items-center gap-2">
-                  {Number(amount) >= 0 ? 'Add Coins' : 'Remove Coins'}
+                <div className="w-full space-y-2">
+                  <div className="flex items-center gap-2">
+                    <Button
+                      onClick={handleAddRemoveCoins}
+                      className="flex-1 h-14 transition-colors flex items-center justify-center font-medium"
+                      variant="default"
+                    >
+                      <div className="flex items-center gap-2">
+                        {Number(amount) >= 0 ? 'Add Coins' : 'Remove Coins'}
+                      </div>
+                    </Button>
+                  </div>
                 </div>
-              </Button>
+              </div>
             </div>
           </CardContent>
         </Card>
@@ -236,6 +256,12 @@ export default function CoinsManager() {
                             <p className="text-sm text-gray-500">
                               {d2s({ dateTime: t2d({ timestamp: transaction.timestamp, timezone: settings.system.timezone }), timezone: settings.system.timezone })}
                             </p>
+                            <TransactionNoteEditor
+                              transactionId={transaction.id}
+                              initialNote={transaction.note}
+                              onSave={handleSaveNote}
+                              onDelete={handleDeleteNote}
+                            />
                           </div>
                           <span
                             className={`font-mono ${transaction.amount >= 0
diff --git a/components/Header.tsx b/components/Header.tsx
index 447c06f..bafd807 100644
--- a/components/Header.tsx
+++ b/components/Header.tsx
@@ -38,17 +38,17 @@ export default function Header({ className }: HeaderProps) {
               <Link href="/coins" className="flex items-center gap-1 sm:gap-2 px-3 py-1.5 bg-white hover:bg-gray-50 dark:bg-gray-700 dark:hover:bg-gray-600 rounded-full transition-colors border border-gray-200 dark:border-gray-600">
                 <Coins className="h-5 w-5 text-yellow-500 dark:text-yellow-400" />
                 <div className="flex items-baseline gap-1 sm:gap-2">
-                  <FormattedNumber 
-                    amount={balance} 
-                    settings={settings} 
-                    className="text-gray-800 dark:text-gray-100 font-medium text-lg" 
+                  <FormattedNumber
+                    amount={balance}
+                    settings={settings}
+                    className="text-gray-800 dark:text-gray-100 font-medium text-lg"
                   />
                   {coinsEarnedToday > 0 && (
                     <span className="text-sm bg-green-50 dark:bg-green-900/30 text-green-700 dark:text-green-400 px-2 py-1 rounded-full border border-green-100 dark:border-green-800">
-                      <FormattedNumber 
-                        amount={coinsEarnedToday} 
-                        settings={settings} 
-                        className="inline" 
+                      <FormattedNumber
+                        amount={coinsEarnedToday}
+                        settings={settings}
+                        className="inline"
                       />
                     </span>
                   )}
diff --git a/components/TransactionNoteEditor.tsx b/components/TransactionNoteEditor.tsx
new file mode 100644
index 0000000..7fe0fb2
--- /dev/null
+++ b/components/TransactionNoteEditor.tsx
@@ -0,0 +1,138 @@
+'use client'
+
+import { useState } from 'react'
+import { Input } from '@/components/ui/input'
+import { Button } from '@/components/ui/button'
+import { Check, Loader2, Pencil, Trash2, X } from 'lucide-react'
+import { toast } from '@/hooks/use-toast'
+
+interface TransactionNoteEditorProps {
+  transactionId: string
+  initialNote?: string
+  onSave: (id: string, note: string) => Promise<void>
+  onDelete: (id: string) => Promise<void>
+}
+
+export function TransactionNoteEditor({
+  transactionId,
+  initialNote = '',
+  onSave,
+  onDelete
+}: TransactionNoteEditorProps) {
+  const [isEditing, setIsEditing] = useState(false)
+  const [noteText, setNoteText] = useState(initialNote)
+  const [isSaving, setIsSaving] = useState(false)
+
+  const handleSave = async () => {
+    const trimmedNote = noteText.trim()
+    if (trimmedNote.length > 200) {
+      toast({
+        title: 'Note too long',
+        description: 'Notes must be less than 200 characters',
+        variant: 'destructive'
+      })
+      return
+    }
+
+    setIsSaving(true)
+    try {
+      await onSave(transactionId, trimmedNote)
+      setIsEditing(false)
+    } catch (error) {
+      toast({
+        title: 'Error saving note',
+        description: 'Please try again',
+        variant: 'destructive'
+      })
+      // Revert to initial value on error
+      setNoteText(initialNote)
+    } finally {
+      setIsSaving(false)
+    }
+  }
+
+  const handleDelete = async () => {
+    setIsSaving(true)
+    try {
+      await onDelete(transactionId)
+      setNoteText(initialNote)
+      setIsEditing(false)
+    } catch (error) {
+      toast({
+        title: 'Error deleting note',
+        description: 'Please try again',
+        variant: 'destructive'
+      })
+    } finally {
+      setIsSaving(false)
+    }
+  }
+
+  if (isEditing) {
+    return (
+      <div className="flex items-center gap-2 mt-1">
+        <Input
+          value={noteText}
+          onChange={(e) => setNoteText(e.target.value)}
+          placeholder="Add a note..."
+          className="w-64"
+          maxLength={200}
+        />
+        <div className="flex items-center gap-1">
+          <Button
+            variant="ghost"
+            size="sm"
+            onClick={handleSave}
+            disabled={isSaving}
+            className="text-green-600 dark:text-green-500 hover:text-green-700 dark:hover:text-green-400 transition-colors"
+            title="Save note"
+          >
+            {isSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Check className="h-4 w-4" />}
+          </Button>
+          <Button
+            variant="ghost"
+            size="sm"
+            onClick={() => {
+              setNoteText(initialNote)
+              setIsEditing(false)
+            }}
+            disabled={isSaving}
+            className="text-red-600 dark:text-red-500 hover:text-red-700 dark:hover:text-red-400 transition-colors"
+            title="Cancel"
+          >
+            <X className="h-4 w-4" />
+          </Button>
+          {initialNote && (
+            <Button
+              variant="ghost"
+              size="sm"
+              onClick={handleDelete}
+              disabled={isSaving}
+              className="text-gray-600 dark:text-gray-500 hover:text-gray-700 dark:hover:text-gray-400 transition-colors"
+              title="Delete note"
+            >
+              {isSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Trash2 className="h-4 w-4" />}
+            </Button>
+          )}
+        </div>
+      </div>
+    )
+  }
+
+  return (
+    <div className="group flex items-center gap-2 mt-1">
+      {noteText && (
+        <span className="text-sm text-gray-500 dark:text-gray-400">
+          {noteText}
+        </span>
+      )}
+      <button
+        onClick={() => setIsEditing(true)}
+        className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
+        aria-label="Edit note"
+      >
+        <Pencil className="h-4 w-4" />
+      </button>
+    </div>
+  )
+}
diff --git a/hooks/useCoins.tsx b/hooks/useCoins.tsx
index f9b5b18..54e611e 100644
--- a/hooks/useCoins.tsx
+++ b/hooks/useCoins.tsx
@@ -1,26 +1,25 @@
 import { useAtom } from 'jotai'
-import { 
-  coinsAtom, 
-  settingsAtom,
+import {
+  coinsAtom,
   coinsEarnedTodayAtom,
   totalEarnedAtom,
   totalSpentAtom,
   coinsSpentTodayAtom,
   transactionsTodayAtom
 } from '@/lib/atoms'
-import { addCoins, removeCoins } from '@/app/actions/data'
+import { addCoins, removeCoins, saveCoinsData } from '@/app/actions/data'
+import { CoinsData } from '@/lib/types'
 import { toast } from '@/hooks/use-toast'
 
 export function useCoins() {
   const [coins, setCoins] = useAtom(coinsAtom)
-  const [settings] = useAtom(settingsAtom)
   const [coinsEarnedToday] = useAtom(coinsEarnedTodayAtom)
   const [totalEarned] = useAtom(totalEarnedAtom)
   const [totalSpent] = useAtom(totalSpentAtom)
   const [coinsSpentToday] = useAtom(coinsSpentTodayAtom)
   const [transactionsToday] = useAtom(transactionsTodayAtom)
 
-  const add = async (amount: number, description: string) => {
+  const add = async (amount: number, description: string, note?: string) => {
     if (isNaN(amount) || amount <= 0) {
       toast({
         title: "Invalid amount",
@@ -29,13 +28,13 @@ export function useCoins() {
       return null
     }
 
-    const data = await addCoins(amount, description)
+    const data = await addCoins(amount, description, 'MANUAL_ADJUSTMENT', undefined, note)
     setCoins(data)
     toast({ title: "Success", description: `Added ${amount} coins` })
     return data
   }
 
-  const remove = async (amount: number, description: string) => {
+  const remove = async (amount: number, description: string, note?: string) => {
     const numAmount = Math.abs(amount)
     if (isNaN(numAmount) || numAmount <= 0) {
       toast({
@@ -45,15 +44,45 @@ export function useCoins() {
       return null
     }
 
-    const data = await removeCoins(numAmount, description)
+    const data = await removeCoins(numAmount, description, 'MANUAL_ADJUSTMENT', undefined, note)
     setCoins(data)
     toast({ title: "Success", description: `Removed ${numAmount} coins` })
     return data
   }
 
+  const updateNote = async (transactionId: string, note: string) => {
+    const transaction = coins.transactions.find(t => t.id === transactionId)
+    if (!transaction) {
+      toast({
+        title: "Error",
+        description: "Transaction not found"
+      })
+      return null
+    }
+
+    const updatedTransaction = {
+      ...transaction,
+      note: note.trim() || undefined
+    }
+
+    const updatedTransactions = coins.transactions.map(t =>
+      t.id === transactionId ? updatedTransaction : t
+    )
+
+    const newData: CoinsData = {
+      ...coins,
+      transactions: updatedTransactions
+    }
+
+    await saveCoinsData(newData)
+    setCoins(newData)
+    return newData
+  }
+
   return {
     add,
     remove,
+    updateNote,
     balance: coins.balance,
     transactions: coins.transactions,
     coinsEarnedToday,
diff --git a/lib/atoms.ts b/lib/atoms.ts
index 09a99d7..9d2a861 100644
--- a/lib/atoms.ts
+++ b/lib/atoms.ts
@@ -5,7 +5,16 @@ import {
   getDefaultCoinsData,
   getDefaultWishlistData
 } from "./types";
-import { getTodayInTimezone, isSameDate, t2d } from "@/lib/utils";
+import { 
+  getTodayInTimezone, 
+  isSameDate, 
+  t2d,
+  calculateCoinsEarnedToday,
+  calculateTotalEarned,
+  calculateTotalSpent,
+  calculateCoinsSpentToday,
+  calculateTransactionsToday
+} from "@/lib/utils";
 
 export const settingsAtom = atom(getDefaultSettings());
 export const habitsAtom = atom(getDefaultHabitsData());
@@ -16,72 +25,31 @@ export const wishlistAtom = atom(getDefaultWishlistData());
 export const coinsEarnedTodayAtom = atom((get) => {
   const coins = get(coinsAtom);
   const settings = get(settingsAtom);
-  const today = getTodayInTimezone(settings.system.timezone);
-  return coins.transactions
-    .filter(transaction =>
-      isSameDate(t2d({ timestamp: transaction.timestamp, timezone: settings.system.timezone }),
-        t2d({ timestamp: today, timezone: settings.system.timezone }))
-    )
-    .reduce((sum, transaction) => {
-      if (transaction.type !== 'HABIT_UNDO' && transaction.amount > 0) {
-        return sum + transaction.amount;
-      }
-      if (transaction.type === 'HABIT_UNDO') {
-        return sum - Math.abs(transaction.amount);
-      }
-      return sum;
-    }, 0);
+  return calculateCoinsEarnedToday(coins.transactions, settings.system.timezone);
 });
 
 // Derived atom for total earned
 export const totalEarnedAtom = atom((get) => {
   const coins = get(coinsAtom);
-  return coins.transactions
-    .filter(t => {
-      if (t.type === 'HABIT_COMPLETION' && t.relatedItemId) {
-        return !coins.transactions.some(undoT =>
-          undoT.type === 'HABIT_UNDO' &&
-          undoT.relatedItemId === t.relatedItemId
-        );
-      }
-      return t.amount > 0 && t.type !== 'HABIT_UNDO';
-    })
-    .reduce((sum, t) => sum + t.amount, 0);
+  return calculateTotalEarned(coins.transactions);
 });
 
 // Derived atom for total spent
 export const totalSpentAtom = atom((get) => {
   const coins = get(coinsAtom);
-  return Math.abs(
-    coins.transactions
-      .filter(t => t.type === 'WISH_REDEMPTION' || t.type === 'MANUAL_ADJUSTMENT')
-      .reduce((sum, t) => sum + (t.amount < 0 ? t.amount : 0), 0)
-  );
+  return calculateTotalSpent(coins.transactions);
 });
 
 // Derived atom for coins spent today
 export const coinsSpentTodayAtom = atom((get) => {
   const coins = get(coinsAtom);
   const settings = get(settingsAtom);
-  const today = getTodayInTimezone(settings.system.timezone);
-  return Math.abs(
-    coins.transactions
-      .filter(t =>
-        isSameDate(t2d({ timestamp: t.timestamp, timezone: settings.system.timezone }),
-          t2d({ timestamp: today, timezone: settings.system.timezone })) &&
-        t.amount < 0
-      )
-      .reduce((sum, t) => sum + t.amount, 0)
-  );
+  return calculateCoinsSpentToday(coins.transactions, settings.system.timezone);
 });
 
 // Derived atom for transactions today
 export const transactionsTodayAtom = atom((get) => {
   const coins = get(coinsAtom);
   const settings = get(settingsAtom);
-  const today = getTodayInTimezone(settings.system.timezone);
-  return coins.transactions.filter(t =>
-    isSameDate(t2d({ timestamp: t.timestamp, timezone: settings.system.timezone }),
-      t2d({ timestamp: today, timezone: settings.system.timezone }))
-  ).length;
+  return calculateTransactionsToday(coins.transactions, settings.system.timezone);
 });
diff --git a/lib/types.ts b/lib/types.ts
index e90bdb4..f60083d 100644
--- a/lib/types.ts
+++ b/lib/types.ts
@@ -24,6 +24,7 @@ export interface CoinTransaction {
   description: string;
   timestamp: string;
   relatedItemId?: string;
+  note?: string;
 }
 
 export interface HabitsData {
diff --git a/lib/utils.test.ts b/lib/utils.test.ts
index 6e19faf..f4e6f92 100644
--- a/lib/utils.test.ts
+++ b/lib/utils.test.ts
@@ -1,5 +1,21 @@
 import { expect, test, describe, beforeAll, afterAll } from "bun:test";
-import { cn, getTodayInTimezone, getNow, getNowInMilliseconds, t2d, d2t, d2s, d2sDate, d2n, isSameDate } from './utils'
+import {
+  cn,
+  getTodayInTimezone,
+  getNow,
+  getNowInMilliseconds,
+  t2d,
+  d2t,
+  d2s,
+  d2sDate,
+  d2n,
+  isSameDate,
+  calculateCoinsEarnedToday,
+  calculateTotalEarned,
+  calculateTotalSpent,
+  calculateCoinsSpentToday,
+} from './utils'
+import { CoinTransaction } from './types'
 import { DateTime } from "luxon";
 
 describe('cn utility', () => {
@@ -93,4 +109,66 @@ describe('datetime utilities', () => {
       expect(isSameDate(date1, date3)).toBe(false)
     })
   })
+
+  describe('transaction calculations', () => {
+    const testTransactions: CoinTransaction[] = [
+      {
+        id: '1',
+        amount: 10,
+        type: 'HABIT_COMPLETION',
+        description: 'Test habit',
+        timestamp: '2024-01-01T12:00:00Z'
+      },
+      {
+        id: '2',
+        amount: -5,
+        type: 'HABIT_UNDO',
+        description: 'Undo test habit',
+        timestamp: '2024-01-01T13:00:00Z',
+        relatedItemId: '1'
+      },
+      {
+        id: '3',
+        amount: 20,
+        type: 'HABIT_COMPLETION',
+        description: 'Another habit',
+        timestamp: '2024-01-01T14:00:00Z'
+      },
+      {
+        id: '4',
+        amount: -15,
+        type: 'WISH_REDEMPTION',
+        description: 'Redeemed wish',
+        timestamp: '2024-01-01T15:00:00Z'
+      },
+      {
+        id: '5',
+        amount: 5,
+        type: 'HABIT_COMPLETION',
+        description: 'Yesterday habit',
+        timestamp: '2023-12-31T23:00:00Z'
+      }
+    ]
+
+    test('calculateCoinsEarnedToday should calculate today\'s earnings including undos', () => {
+      const result = calculateCoinsEarnedToday(testTransactions, 'UTC')
+      expect(result).toBe(25) // 10 + 20 - 5 (including the -5 undo)
+    })
+
+    test('calculateTotalEarned should calculate lifetime earnings including undos', () => {
+      const result = calculateTotalEarned(testTransactions)
+      expect(result).toBe(30) // 10 + 20 + 5 - 5 (including the -5 undo)
+    })
+
+    test('calculateTotalSpent should calculate total spent excluding undos', () => {
+      const result = calculateTotalSpent(testTransactions)
+      expect(result).toBe(15) // Only the 15 wish redemption (excluding the 5 undo)
+    })
+
+    test('calculateCoinsSpentToday should calculate today\'s spending excluding undos', () => {
+      const result = calculateCoinsSpentToday(testTransactions, 'UTC')
+      expect(result).toBe(15) // Only the 15 wish redemption (excluding the 5 undo)
+    })
+
+  })
 })
diff --git a/lib/utils.ts b/lib/utils.ts
index 03bc7bc..3991342 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -1,7 +1,7 @@
 import { clsx, type ClassValue } from "clsx"
 import { twMerge } from "tailwind-merge"
 import { DateTime } from "luxon"
-import { Habit } from '@/lib/types'
+import { Habit, CoinTransaction } from '@/lib/types'
 
 export function cn(...inputs: ClassValue[]) {
   return twMerge(clsx(inputs))
@@ -66,17 +66,17 @@ export function normalizeCompletionDate(date: string, timezone: string): string
   return DateTime.fromFormat(date, 'yyyy-MM-dd', { zone: timezone }).toUTC().toISO()!;
 }
 
-export function getCompletionsForDate({ 
-  habit, 
-  date, 
-  timezone 
-}: { 
-  habit: Habit, 
-  date: DateTime | string, 
-  timezone: string 
+export function getCompletionsForDate({
+  habit,
+  date,
+  timezone
+}: {
+  habit: Habit,
+  date: DateTime | string,
+  timezone: string
 }): number {
   const dateObj = typeof date === 'string' ? DateTime.fromISO(date) : date
-  return habit.completions.filter((completion: string) => 
+  return habit.completions.filter((completion: string) =>
     isSameDate(t2d({ timestamp: completion, timezone }), dateObj)
   ).length
 }
@@ -97,27 +97,79 @@ export function getCompletedHabitsForDate({
   })
 }
 
-export function isHabitCompletedToday({ 
-  habit, 
-  timezone 
-}: { 
-  habit: Habit, 
-  timezone: string 
+export function isHabitCompletedToday({
+  habit,
+  timezone
+}: {
+  habit: Habit,
+  timezone: string
 }): boolean {
   const today = getTodayInTimezone(timezone)
   const completionsToday = getCompletionsForDate({ habit, date: today, timezone })
   return completionsToday >= (habit.targetCompletions || 1)
 }
 
-export function getHabitProgress({ 
-  habit, 
-  timezone 
-}: { 
-  habit: Habit, 
-  timezone: string 
+export function getHabitProgress({
+  habit,
+  timezone
+}: {
+  habit: Habit,
+  timezone: string
 }): number {
   const today = getTodayInTimezone(timezone)
   const completionsToday = getCompletionsForDate({ habit, date: today, timezone })
   const target = habit.targetCompletions || 1
   return Math.min(100, (completionsToday / target) * 100)
 }
+
+export function calculateCoinsEarnedToday(transactions: CoinTransaction[], timezone: string): number {
+  const today = getTodayInTimezone(timezone);
+  return transactions
+    .filter(transaction =>
+      isSameDate(t2d({ timestamp: transaction.timestamp, timezone }),
+        t2d({ timestamp: today, timezone })) &&
+      (transaction.amount > 0 || transaction.type === 'HABIT_UNDO')
+    )
+    .reduce((sum, transaction) => sum + transaction.amount, 0);
+}
+
+export function calculateTotalEarned(transactions: CoinTransaction[]): number {
+  return transactions
+    .filter(transaction => 
+      transaction.amount > 0 || transaction.type === 'HABIT_UNDO'
+    )
+    .reduce((sum, transaction) => sum + transaction.amount, 0);
+}
+
+export function calculateTotalSpent(transactions: CoinTransaction[]): number {
+  return Math.abs(
+    transactions
+      .filter(transaction => 
+        transaction.amount < 0 &&
+        transaction.type !== 'HABIT_UNDO'
+      )
+      .reduce((sum, transaction) => sum + transaction.amount, 0)
+  );
+}
+
+export function calculateCoinsSpentToday(transactions: CoinTransaction[], timezone: string): number {
+  const today = getTodayInTimezone(timezone);
+  return Math.abs(
+    transactions
+      .filter(transaction =>
+        isSameDate(t2d({ timestamp: transaction.timestamp, timezone }),
+          t2d({ timestamp: today, timezone })) &&
+        transaction.amount < 0 &&
+        transaction.type !== 'HABIT_UNDO'
+      )
+      .reduce((sum, transaction) => sum + transaction.amount, 0)
+  );
+}
+
+export function calculateTransactionsToday(transactions: CoinTransaction[], timezone: string): number {
+  const today = getTodayInTimezone(timezone);
+  return transactions.filter(t =>
+    isSameDate(t2d({ timestamp: t.timestamp, timezone }),
+      t2d({ timestamp: today, timezone }))
+  ).length;
+}
diff --git a/package-lock.json b/package-lock.json
index 8f4026a..5f324e7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "habittrove",
-  "version": "0.1.12",
+  "version": "0.1.16",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "habittrove",
-      "version": "0.1.12",
+      "version": "0.1.16",
       "dependencies": {
         "@emoji-mart/data": "^1.2.1",
         "@emoji-mart/react": "^1.1.1",