Added all of chat-topics, persona;-history, projects, receipts and vehicle. This is the knowledge base section after all....

This commit is contained in:
2026-05-26 16:53:50 +10:00
parent 85f78332d1
commit 6ada86fe73
91 changed files with 3983 additions and 26 deletions
@@ -0,0 +1,282 @@
# Energy Conservation / Local Power Project
Date: 2026-05-25
Context: Michael is exploring practical local-first energy resilience: tidal/current capture in NSW coastal inlets, especially Wagonga Inlet/Narooma, then broader household solar and storage strategy.
## Core framing
- Solar generation is comparatively mature and cheap at household scale.
- The hard problem is storage, especially storing enough usable energy for high-demand household loads and multi-day/seasonal mismatch.
- Tidal/current energy is attractive because it is predictable and locally visible in coastal inlets, but usable power depends on **current velocity**, not just total water volume.
- Best near-term path is not “replace grid with one magic system”, but layered resilience:
- use solar directly when available
- store heat as heat where possible
- store premium electricity in batteries
- keep grid/generator as rare backup unless full off-grid is explicitly worth the cost
## Tidal/current turbine exploration
### Initial question
Michael observed that NSW coastal inlets move huge volumes of water daily and wondered whether tidal propellers/turbines could capture useful power, using Wagonga Inlet as a local example.
### Existing Australian work found
Key project: **AUSTEn / Tidal Energy in Australia**
- Led by University of Tasmania / Australian Maritime College with CSIRO, University of Queensland, ARENA and industry partners including MAKO, Sabella, and SIMEC Atlantis.
- Produced national tidal resource modelling at ~500 m resolution.
- Focused field/case studies at Banks Strait TAS and Clarence Strait NT.
- Found Australias strongest tidal stream opportunities mostly in northern Australia, plus Banks Strait TAS and Port Phillip Heads VIC.
- Even promising Australian sites commonly show max tidal velocities around ~22.8 m/s, whereas many international commercial projects prefer nearer ~4 m/s.
- Australian tidal energy remains mostly research/demonstration scale; no large commercial tidal farm currently operating domestically.
Other examples:
- **MAKO tidal turbines**: Australian small turbine work/trials including Tamar River TAS, Gladstone Port QLD, Singapore and overseas activity.
- **Tidal Turbine Reef feasibility study**: WA concept around One Arm Point / King Sound conditions.
- International comparison: MeyGen in Scotland demonstrates tidal stream can work at commercial/demonstration array scale, but in stronger-current sites than typical NSW inlets.
### Main physical constraints for NSW inlets
- **Velocity cube law**: available power scales with current velocity cubed.
- 1 m/s = baseline
- 2 m/s = 8× power
- 3 m/s = 27× power
- 4 m/s = 64× power
- Large tidal prism does not automatically mean useful turbine power if the flow is spread across too large a section or too slow.
- Tidal generation is intermittent through the cycle: flood peak, slack high, ebb peak, slack low.
- NSW inlets are sediment machines: shoaling, scour, storm response, bar movement, training-wall effects.
- Turbine frames/fences could change local flow, causing scour holes, shoaling, altered navigation depths, bank erosion or changed flushing.
- Best current is often in the navigable channel, creating boat-safety and approval conflicts.
- Marine ecology concerns: fish strike, rays/seals/dolphins/turtles depending site, noise/vibration, cable EMF, benthic disturbance, altered flushing/salinity/sediment.
- Maintenance is harsh: salt, biofouling, weed, fishing line, flood debris, logs, storm access, corrosion.
- Economics must compete with very cheap solar and batteries.
### Small turbine concept
Michael asked whether 10 m turbines are necessary or whether 0.51 m turbines could be deployed in strings across/along fast channel sections.
Conclusion:
- Small turbines are the more plausible direction for Wagonga-style inlets.
- Concept should look less like an underwater wind farm and more like modular, guarded, removable tidal cartridges.
- Avoid spanning the main boat lane initially.
- Prefer mounting near existing structures where possible:
- training walls
- wharf piles
- bridge-adjacent structures
- edge-flow zones outside marked navigation channel
- Design requirements:
- removable/lift-out frames
- guarded rotors
- storm/flood retrieval
- low-cost serviceability
- local battery/load nearby
- minimal seabed works if possible
Rough 1 m turbine power at useful efficiency:
| Current speed | Approx output per 1 m turbine |
|---:|---:|
| 0.8 m/s | ~70 W |
| 1.0 m/s | ~140 W |
| 1.2 m/s | ~240 W |
| 1.5 m/s | ~475 W |
| 2.0 m/s | ~1.1 kW |
| 2.5 m/s | ~2.2 kW |
A 0.5 m turbine has roughly one-quarter the swept area, so roughly one-quarter the power.
Interpretation:
- If sites reliably hit ~1.52 m/s, small turbine strings become technically interesting.
- If most sites are <1 m/s, the concept becomes mostly demo/education unless hardware is extremely cheap.
### Wagonga Inlet data found
Public NSW estuary data:
- Estuary volume: ~39,101 ML
- Average depth: ~5.7 m
- Tidal prism measured 1986:
- ebb: ~6,340 ML
- flood: ~6,640 ML
- Local tidal range:
- ebb entry: ~1.49 m
- flood entry: ~1.24 m
- Entrance is open/trained.
- Downstream channel often described around ~35 m below MSL, with shallower shoals upstream.
- Flood Data Portal has TUFLOW model input/output for Wagonga/Kianga/Dalmeny, but outputs are large (~1 GB) and flood-focused rather than a simple normal-tide current table.
- Public searches did not uncover a clean ADCP/current-speed dataset for normal tidal currents in Wagonga.
Back-calculated estimate from ebb prism:
- 6,340 ML = 6.34 million m³ over roughly half-tide (~6.2 h).
- Average exchange flow: ~284 m³/s.
- Estimated sinusoidal peak tidal flow: ~445 m³/s.
Estimated peak speed depends on channel cross-sectional area:
| Flow cross-section | Estimated peak speed |
|---:|---:|
| 150 m² | ~3.0 m/s / 5.8 knots |
| 200 m² | ~2.2 m/s / 4.3 knots |
| 250 m² | ~1.8 m/s / 3.5 knots |
| 300 m² | ~1.5 m/s / 2.9 knots |
| 400 m² | ~1.1 m/s / 2.2 knots |
| 500 m² | ~0.9 m/s / 1.7 knots |
Working estimate:
- Fast entrance/channel sections of Wagonga are plausibly ~12+ m/s on stronger tides, with local faster jets possible near constrictions, walls, bridge/channel features, and ebb-bar conditions.
- This is not enough for design; it is enough to justify measurement.
### Measurement path
Need current mapping before turbine design.
Best instrument: **ADCP — Acoustic Doppler Current Profiler**
- Uses angled acoustic beams and Doppler shift from particles/bubbles/sediment in the water.
- Measures current speed/direction at multiple depths, giving a vertical profile.
- Can be:
- bottom-mounted looking upward for days/weeks
- boat-mounted looking downward during transects
- side-mounted from wharf/bridge/pile
For Wagonga, useful survey points:
- Narooma bridge area
- downstream training wall edges
- entrance channel
- flood-tide delta edge
- spring vs neap tide comparison
- surface/mid-depth/near-bed velocity profiles
Cheaper first-pass option: calibrated propeller/impeller current meter.
- A small free-spinning rotor can estimate current speed from RPM.
- Use Hall sensor / optical sensor / encoder / small generator frequency.
- Must be calibrated by towing at known GPS speeds in calm water.
- Best for finding promising spots, not for final engineering/approval.
- Avoid loading it as a generator during measurement, because electrical load changes RPM and corrupts speed estimates.
## Solar and battery/storage discussion
### Michaels situation
- Household has ~20 kW rooftop solar.
- Often produces more than household uses during peak days/weeks/seasons.
- No battery yet.
- Concern: cannot find a battery system large enough / economical enough to store all excess.
- Specific pain loads:
- hot water
- cooking
- other high-power/high-energy thermal loads
- Rest of household load seems manageable.
### Core conclusion
- Household solar generation is mostly solved.
- Storage is the limiting problem.
- Batteries are good for same-day/overnight shifting and resilience.
- Batteries are poor/economically awkward for storing all surplus, multi-week deficits, seasonal mismatch, or repeated large thermal loads.
- Chasing “store all excess solar” is likely the wrong target.
- Better target: maximise useful self-consumption and resilience without overbuilding storage that sits idle much of the year.
### Recommended hierarchy
1. **Direct use first**
- Shift flexible loads into solar hours:
- dishwasher
- washing
- EV charging if relevant
- pumps/pool/workshop loads
- appliance scheduling
2. **Store heat as heat**
- Hot water is a cheap thermal battery.
- Prefer:
- heat pump hot water
- solar diverter to hot water tank
- larger hot water tank
- controlled daytime heating
- Thermal storage is much cheaper per stored kWh than lithium for hot water.
3. **Battery for premium electricity**
- Use battery for loads that truly need electricity:
- fridge/freezer
- lights
- computers/network
- pumps
- security/comms
- short cooking bursts
- overnight baseload
- outage backup
4. **Grid/generator as rare backup**
- 9095% self-powered is much cheaper than 100%.
- The last 510% is where system cost explodes.
### Battery sizing thoughts
- Practical sweet spot for a 20 kW solar household may be roughly **2040 kWh** battery, depending actual load profile.
- For serious resilience, maybe **5060 kWh**, but only with a specific goal and inverter/load analysis.
- Beyond that, caution: large batteries can be expensive and underutilised for much of the year.
### Cooking
- Cooking is high power but not always huge total energy.
- Induction can pull hard, but often for short durations.
- Design issue is both:
- battery capacity (kWh: how long)
- inverter power/surge (kW: how hard)
- A household can cook from battery/solar, but making it feel normal requires adequate inverter capacity and careful load management.
### System philosophy
Do not store all solar surplus as electricity.
Better overall design:
- 20 kW solar remains core generation.
- Add battery sized for overnight/resilience, not full seasonal capture.
- Convert hot water to controlled solar/heat-pump thermal storage.
- Shift flexible loads into solar windows.
- Retain grid/export/backup for the economically ugly edge cases.
Working phrase:
> Use solar directly, store heat cheaply, store electricity selectively, and only buy enough battery to cover expensive/important overnight and outage loads.
## Open questions / next research steps
### Tidal/Wagonga
- Can actual normal-tide current data be obtained from:
- NSW/Eurobodalla hydrodynamic studies
- Flood Data Portal TUFLOW outputs
- Marine Rescue / local navigation records
- council coastal management planners
- universities / consultants
- Is there a practical ADCP hire/service path locally?
- Could a low-cost impeller logger be built and calibrated for initial site reconnaissance?
- Which edge/structure locations are fastest while avoiding the marked navigation channel?
### Household solar/storage
- Pull actual household interval data if available:
- solar generation by 5/15/30 min interval
- import/export
- hot water load timing
- cooking peaks
- winter vs summer profiles
- Model battery sizes: 13.5, 20, 30, 40, 60 kWh.
- Model hot water options:
- heat pump hot water
- resistive diverter
- larger tank
- controlled relay/timer
- Identify practical inverter requirements for cooking/backup circuits.
- Decide whether goal is cost saving, blackout resilience, grid independence, or technical sovereignty; sizing changes depending priority.
+67
View File
@@ -0,0 +1,67 @@
# MANNING FAMILY SUPERANNUATION FUND
## INVESTMENT STRATEGY
**ESTABLISHMENT DATE:** 12/02/2026
### 1. INVESTMENT OBJECTIVE
The objective of the Fund is to maximize member retirement benefits by achieving a long-term average return that exceeds the Consumer Price Index (CPI) by at least **4% to 5% per annum** over rolling 5-10 year periods. The Trustees accept a **High** risk profile to achieve this growth.
### 2. ASSET ALLOCATION RANGES
To achieve the objective, the Fund will invest within the following ranges:
| Asset Class | Min % | Max % | Target % |
| :--- | :--- | :--- | :--- |
| **Cash & Term Deposits** | 0% | 100% | 10% |
| **Cryptocurrency (Bitcoin)** | 0% | 70% | 60% |
| **Tokenized Precious Metals (Gold/Silver)** | 0% | 40% | 20% |
| **Australian & International Equities** | 0% | 50% | 10% |
| **Property / Infrastructure** | 0% | 50% | 0% |
*(Note: The Trustees acknowledge that market volatility may cause actual weightings to drift outside these ranges temporarily. Such drift will be addressed at the next Trustee meeting or annual review.)*
### 3. INVESTMENT RATIONALE
#### **A. Cryptocurrency (Bitcoin) - High Growth Allocation**
The Trustees have determined to allocate the majority (approx. 60%) of the Fund to Bitcoin.
* **Rationale:** The Trustees view Bitcoin as a long-term store of value ("digital gold") and a high-growth asset class that offers protection against monetary debasement.
* **Risk Mitigation:**
* The Fund will hold Bitcoin primarily in **cold storage/hardware wallets** (self-custody) to eliminate exchange counterparty risk.
* The Trustees possess the technical expertise to manage private keys securely.
#### **B. Tokenized Precious Metals - Inflation Hedge**
The Fund will invest approximately 20% in Gold and Silver.
* **Method:** The Fund may utilize tokenized assets (such as **Gold Standard (AUS)** and **Silver Standard (AGS)**) purchased via Australian exchanges (e.g., CoinSpot) and backed by physical metal (e.g., via Ainslie Wealth).
* **Rationale:** This provides exposure to the stability and inflation-hedging properties of physical metals without the storage costs, insurance complexities, and illiquidity of holding physical bullion bars.
* **Risk Note:** The Trustees acknowledge this introduces counterparty risk (the issuer holding the metal) and technology risk (smart contract risk), which is accepted in exchange for liquidity.
#### **C. Equities (Stocks) - Diversified Growth**
The Fund will invest approximately 10% in Australian or International equities (via platforms like Stake) to provide dividend income and capital growth distinct from the cryptocurrency market.
#### **D. Cash - Liquidity**
The Fund will maintain sufficient cash (in the CMA) to cover annual taxes, accounting fees, and statutory levies.
### 4. DIVERSIFICATION WARNING
The Trustees acknowledge that the Fund's heavy weighting towards Bitcoin (60%) and Precious Metals (20%) represents a concentrated portfolio. The Trustees have considered the lack of traditional diversification and **accept this risk**, believing that the potential for superior returns in these specific asset classes outweighs the benefits of broad diversification at this stage of the Funds lifecycle.
### 5. LIQUIDITY & CASH FLOW
The Trustees have reviewed the cash flow requirements of the Fund. As the members are in the accumulation phase and not currently drawing a pension, the Fund does not require high levels of regular liquidity. The Cash allocation is sufficient to meet running costs.
### 6. INSURANCE
The Trustees have considered holding Life, TPD, and Trauma insurance for members within the Fund.
* **Decision:** The Trustees have decided **NOT** to hold insurance inside the Fund at this time.
* **Reason:** Members hold adequate insurance policies outside the Fund / or have determined insurance is not cost-effective. (The Trustees will review this annually).
### 7. EXECUTION
**SIGNED** by the Trustees as being an accurate reflection of the Funds investment strategy.
_________________________
**Michael Howe Manning**
_________________________
**Catherine Jane Manning**
@@ -0,0 +1,140 @@
# MANNING FAMILY SUPERANNUATION FUND
## INVESTMENT STRATEGY — UPDATED VERSION
**Original establishment date:** 12/02/2026
**Updated/reviewed:** 17/05/2026
**Purpose of update:** Reflect actual recorded SMSF holdings, current cash position, and proposed A$20,000 physical precious metals allocation.
---
### 1. INVESTMENT OBJECTIVE
The objective of the Fund is to maximise member retirement benefits by achieving long-term growth above inflation over rolling 5-10 year periods. The Trustees accept a **High** risk profile in pursuit of this objective, including exposure to Bitcoin, growth equities, technology/AI-related businesses, and defensive hard assets.
The Trustees acknowledge that the Fund is growth-oriented and may experience significant volatility over shorter periods.
---
### 2. CURRENT / PROPOSED ASSET ALLOCATION
The following table reflects the Funds current locally recorded holdings and the proposed physical precious metals purchase of up to **A$20,000**.
| Asset Class | Current / Proposed Holding | Approx. Amount | Approx. % of Fund | Strategic Role |
| :--- | :--- | ---: | ---: | :--- |
| **Cryptocurrency — Bitcoin** | 1.37685299877 BTC | A$149,990.18 | 64.5% | High-growth / digital store-of-value allocation |
| **Australian & International Equities** | NDQ, GOOGL, NVDA, TSLA | A$42,470.13 | 18.3% | Growth exposure, technology/AI, global equities diversification |
| **Physical Precious Metals — Gold/Silver** | Proposed physical allocation | A$20,000.00 | 8.6% | Defensive diversifier / hard-asset store of value |
| **Cash / Unallocated** | Cash and broker balances | A$20,026.69 | 8.6% | Liquidity, fees, tax, future investment flexibility |
| **Total** | | **A$232,487.00** | **100.0%** | |
*Amounts are based on recorded SMSF cost base / available capital records and are not live market valuations. Actual percentages may vary with market movement, FX movement, fees, and future transactions.*
---
### 3. STRATEGIC ASSET ALLOCATION RANGES
To support the Funds objective, the Trustees may invest within the following broad ranges:
| Asset Class | Min % | Max % | Current / Proposed % | Target / Preferred Position |
| :--- | :---: | :---: | :---: | :---: |
| **Cash & Term Deposits** | 0% | 30% | 8.6% | 5-15% |
| **Cryptocurrency — Bitcoin** | 0% | 70% | 64.5% | 50-65% |
| **Physical Precious Metals — Gold/Silver** | 0% | 20% | 8.6% | 5-10% |
| **Australian & International Equities** | 0% | 50% | 18.3% | 15-30% |
| **Property / Infrastructure** | 0% | 30% | 0.0% | 0-10% |
The Trustees acknowledge that market volatility may cause actual weightings to drift outside preferred positions. Any material drift will be considered at the next Trustee meeting or annual investment strategy review.
---
### 4. INVESTMENT RATIONALE
#### A. Cryptocurrency — Bitcoin
The Trustees have determined that Bitcoin may form a significant part of the Funds long-term growth strategy.
- **Rationale:** Bitcoin is viewed as a scarce digital asset and potential long-term store of value, with potential protection against monetary debasement.
- **Risk acknowledgement:** Bitcoin is highly volatile and may experience large drawdowns. The Trustees accept this risk as part of the Funds high-growth profile.
- **Control and custody:** The Trustees must maintain appropriate records of acquisition, ownership, custody, wallet/exchange arrangements, and annual valuation evidence.
#### B. Australian & International Equities
The Fund holds Australian and international equity exposure, including broad Nasdaq exposure and selected direct US technology/growth shares.
- **Current recorded holdings:** NDQ, GOOGL, NVDA, TSLA.
- **Rationale:** Equities provide exposure to productive businesses, global technology platforms, AI-related growth, cloud computing, semiconductors, automation, and capital growth opportunities outside cryptocurrency.
- **Risk acknowledgement:** The Trustees acknowledge equity market risk, valuation risk, concentration risk in technology/growth businesses, and foreign currency risk for US holdings.
#### C. Physical Precious Metals — Gold/Silver
The Trustees have resolved to allow a physical precious metals allocation of up to **A$20,000**, primarily gold and silver.
- **Rationale:** Physical precious metals may provide defensive diversification against equity market risk, currency debasement, inflation, financial-system stress, and concentration in Bitcoin and technology/growth assets.
- **Preferred structure:** Allocated physical bullion held with a reputable Australian bullion provider/custodian is preferred over tokenised, pooled, or personally held arrangements.
- **Gold/Silver weighting:** Gold is expected to form the core of the allocation due to its monetary/store-of-value characteristics. Silver may be held as a smaller complementary allocation.
- **Records required:** Purchase invoices, holding statements, storage/custody evidence, insurance details where available, annual valuations, and evidence of SMSF ownership should be retained.
#### D. Cash & Liquidity
The Fund will maintain sufficient cash to meet expected expenses, tax obligations, accounting/audit fees, statutory costs, and future investment opportunities.
The Trustees acknowledge that reducing cash to fund the precious metals allocation still leaves a modest cash reserve and will review liquidity as part of ongoing fund management.
---
### 5. DIVERSIFICATION AND CONCENTRATION RISK
The Trustees acknowledge that the Fund remains concentrated, particularly in Bitcoin and technology/growth-oriented assets. The Trustees have considered the risks of inadequate diversification and accept them in the context of the members objectives, time horizon, risk tolerance, and preference for long-term growth.
The proposed physical precious metals allocation is intended to improve diversification by adding a defensive hard-asset component that is not directly reliant on Bitcoin, equity markets, corporate earnings, or technology-sector valuations.
---
### 6. LIQUIDITY & CASH FLOW
The Trustees have reviewed the cash flow requirements of the Fund. As the members are in the accumulation phase and are not currently drawing pensions, the Fund does not require high levels of regular liquidity.
The Trustees will retain sufficient cash or liquid assets to meet expected expenses and statutory obligations. The Trustees acknowledge that Bitcoin, equities, and precious metals can be volatile and that liquidity timing may vary during stressed market conditions.
---
### 7. SOLE PURPOSE, OWNERSHIP, AND RECORD KEEPING
All investments must be made and maintained for the sole purpose of providing retirement benefits to members, or death benefits where applicable.
The Trustees confirm that investments must:
- be permitted by the Funds trust deed and superannuation law;
- be made on an arms-length basis and at market value;
- show clear legal ownership by the Fund;
- be supported by appropriate source documents, transaction records, valuations, custody records, and audit evidence;
- be reviewed regularly against the Funds investment strategy, risk profile, liquidity needs, and member retirement objectives.
---
### 8. INSURANCE
The Trustees have considered whether the Fund should hold Life, TPD, or other insurance cover for members.
**Decision:** The Trustees have decided **not** to hold insurance inside the Fund at this time.
**Reason:** The Trustees have considered member circumstances, existing arrangements, cost-effectiveness, and the Funds objectives. This decision will be reviewed periodically or when member circumstances materially change.
---
### 9. REVIEW
The Trustees will review this investment strategy at least annually and when there is a material change to the Funds circumstances, member circumstances, asset allocation, liquidity needs, or investment objectives.
---
### 10. EXECUTION
**SIGNED** by the Trustees as being an accurate reflection of the Funds updated investment strategy.
_________________________
**Michael Howe Manning**
_________________________
**Catherine Jane Manning**
+20 -1
View File
@@ -32,7 +32,9 @@ Working area for Michael's SMSF-related records, tools, decisions, and reference
## Key decisions captured
- Use a dedicated SMSF tracker workbook rather than ad hoc notes.
- Keep **actual executed transactions** separate from allocation proposals/ideas.
- Use `transactions/actual-transactions.csv` as the canonical analyzable ledger for tax/reporting work.
- Use `smsf.sqlite` as the canonical structured ledger for tax/reporting work.
- Generate spreadsheets/CSV/PDF reports on demand from the DB for accountant/auditor/government requirements; do not manually maintain parallel spreadsheets as source-of-truth records.
- Keep `transactions/actual-transactions.csv` as a preserved legacy/import seed until the DB workflow is fully established.
- Prioritise AUD reporting support for SMSF/admin/accounting use.
- Include a cleaner accountant/auditor-facing summary tab separate from detailed working sheets.
- Keep this project under `knowledge/projects/smsf/` as the long-term checkpoint location.
@@ -45,3 +47,20 @@ Working area for Michael's SMSF-related records, tools, decisions, and reference
## Notes
- This is a documentation and operational support area, not tax or financial advice.
- Final compliance/accounting treatment should still be validated with the SMSF accountant/auditor.
## SQLite structured ledger layer
Created 2026-05-14:
- `smsf.sqlite` — structured SQLite ledger for audit/tax reporting support.
- `smsf-schema.sql` — schema dump for review/versioning.
- `scripts/build_smsf_db.py` — repeatable importer/rebuilder from current CSV/source documents.
- `reports/sqlite-migration-verification.md` — migration counts/totals verification.
- `reports/holdings-from-db.md` — holdings generated from SQLite.
- `reports/sqlite-usage.md` — useful queries and process notes.
Existing Markdown, CSV, workbook, and source documents were preserved. The DB is intended to become the structured query/reporting layer while source documents remain the immutable audit evidence.
## Reporting/export convention
Going forward, SMSF updates and processing should go directly into `smsf.sqlite`. Spreadsheets are generated on demand for a specific request, requirement, accountant/auditor format, or review purpose. This avoids manually maintaining duplicate sources of truth.
@@ -0,0 +1 @@
eb6b618e607453d23e64824ad293020d0880a693779156f031ae5c1cedd6823d knowledge/projects/smsf/backups/pre-sqlite-migration-20260514-203133.tar.gz
@@ -0,0 +1,74 @@
# Decision Point — Physical Precious Metals Allocation and Investment Strategy Update
## Date
2026-05-17
## Decision summary
The trustees are considering/approving a modest physical precious metals allocation of up to **A$20,000** within the SMSF, primarily across gold and silver, as a defensive diversifier against the Funds existing Bitcoin and technology/AI-heavy growth exposure.
## Current recorded SMSF position before proposed metals purchase
Based on local SMSF records / `smsf.sqlite`:
| Asset | Ticker | Quantity | Cost base / cash out |
|---|---:|---:|---:|
| Bitcoin | BTC | 1.37685299877 | A$149,990.18 |
| BetaShares Nasdaq 100 ETF | NDQ | 429 | A$24,953.64 |
| Alphabet Inc Class A | GOOGL | 12 | A$6,571.79 |
| NVIDIA Corp | NVDA | 22 | A$6,431.72 |
| Tesla Inc | TSLA | 8 | A$4,512.98 |
- Confirmed invested cost base: **A$192,460.31**
- Locally documented total available capital: **A$232,487.00**
- Implied cash/unallocated before metals: **A$40,026.69**
## Proposed allocation after A$20,000 physical metals purchase
| Asset Class | Approx. Amount | Approx. % of Fund | Role |
|---|---:|---:|---|
| Cryptocurrency — Bitcoin | A$149,990.18 | 64.5% | High-growth / digital store-of-value |
| Australian & International Equities | A$42,470.13 | 18.3% | Growth / technology / AI exposure |
| Physical Precious Metals — Gold/Silver | A$20,000.00 | 8.6% | Defensive hard-asset diversifier |
| Cash / Unallocated | A$20,026.69 | 8.6% | Liquidity / expenses / future flexibility |
| **Total** | **A$232,487.00** | **100.0%** | |
## Metals implementation preference
- Prefer **physical precious metals** over tokenised metals.
- Prefer **allocated physical bullion** held with a reputable Australian bullion provider/custodian.
- Avoid personally mixed custody unless there is a strong reason and audit/storage/insurance evidence is robust.
- Gold should be the core allocation; silver may be a smaller complementary allocation.
- Indicative split discussed: **70-80% gold / 20-30% silver**.
## Rationale
- Adds defensive diversification to a portfolio currently concentrated in BTC and tech/AI growth assets.
- Provides hard-asset/store-of-value exposure not directly reliant on corporate earnings, Nasdaq valuations, Bitcoin adoption, or AI/tech execution.
- A$20,000 is meaningful but still modest at approximately **8.6%** of documented total available capital.
- A$30,000 was discussed but considered the upper edge for now because it would materially reduce remaining cash flexibility.
## Key risks acknowledged
- Precious metals price volatility.
- Buy/sell spreads.
- Storage and insurance costs.
- Custodian/counterparty risk if held with a bullion provider.
- Liquidity timing risk under stressed market conditions.
- Need for clear SMSF ownership, annual valuation evidence, and audit-ready records.
## SMSF / ATO record-keeping requirements noted
The investment should:
- be consistent with the Funds written investment strategy;
- satisfy the sole purpose test;
- be acquired on an arms-length basis at market value;
- be clearly owned by the SMSF;
- retain invoices, storage/custody records, insurance details where applicable, annual valuations, and holding statements;
- be reviewed periodically alongside liquidity, diversification, and member retirement objectives.
## Related document created
Updated investment strategy document created:
- `knowledge/projects/smsf/InvestmentStrategy-Updated-2026-05-17.md`
Original preserved:
- `knowledge/projects/smsf/InvestmentStrategy-Completed-2026.md`
## Notes
This is an SMSF operational/record-keeping checkpoint and decision record, not licensed financial or tax advice. Final accounting/compliance treatment should be checked with the SMSF accountant/auditor where required.
+14
View File
@@ -0,0 +1,14 @@
# SMSF Holdings from SQLite
Generated from `smsf.sqlite`.
| Ticker | Name | Asset class | Market | Quantity | Native cost base | AUD cost base | Broker |
|---|---|---|---|---:|---:|---:|---|
| AMZN | Amazon.com Inc | equity | NASDAQ | 18 | USD 4782.00 | AUD 6605.78 | Stake |
| BTC | Bitcoin | crypto | BTC/AUD | 1.3768529988 | AUD 149990.18 | AUD 149990.18 | CoinSpot |
| GOOGL | Alphabet Inc Class A | equity | NASDAQ | 12 | USD 4757.40 | AUD 6571.79 | Stake |
| NDQ | BetaShares NASDAQ 100 ETF | equity | ASX | 429 | AUD 24953.64 | AUD 24953.64 | Stake |
| NVDA | NVIDIA Corp | equity | NASDAQ | 22 | USD 4656.00 | AUD 6431.72 | Stake |
| TSLA | Tesla Inc | equity | NASDAQ | 8 | USD 3267.00 | AUD 4512.98 | Stake |
Note: USD holdings use AUD cost bases from captured Stake FX evidence where available.
@@ -0,0 +1,23 @@
# SMSF SQLite Migration Verification
Generated: 2026-05-14T20:34:36+10:00
## Counts
- Transactions imported: **24**
- Trade lots created: **24**
- Source documents indexed: **7**
## Native totals by currency
- AUD: 21 transaction(s), total 174943.82
- USD: 3 transaction(s), total 12680.40
## AUD reporting total where known
- Total AUD-equivalent transaction cash amount: **A$192460.31**
## Notes
- Existing source documents, Markdown files, CSV ledger, and workbook were not deleted or moved.
- This database is record-keeping support only; accountant/auditor should confirm final classifications and reporting treatment.
+55
View File
@@ -0,0 +1,55 @@
# SMSF SQLite Usage Notes
Database: `knowledge/projects/smsf/smsf.sqlite`
The SQLite database is now the structured ledger layer for audit/tax reporting support. Existing CSV, Markdown, workbook, and source documents remain in place.
## Key tables
- `transactions` — canonical transaction/event ledger imported from `transactions/actual-transactions.csv`.
- `trade_lots` — open acquisition lots auto-created from executed buy transactions.
- `cash_movements` — bank/platform cash movements, including the initial Stake FX deposit record.
- `fx_rates` — explicit FX rates used for AUD reporting.
- `source_documents` — indexed evidence files with SHA256 hashes.
- `transaction_documents` — links transactions to evidence.
- `decisions` — accounting/record-keeping decisions and assumptions.
- `audit_log` — migration/import events.
## Useful queries
```bash
sqlite3 -header -column knowledge/projects/smsf/smsf.sqlite \
"SELECT * FROM v_holdings;"
```
```bash
sqlite3 -header -column knowledge/projects/smsf/smsf.sqlite \
"SELECT financial_year, asset_class, total_currency, transaction_count, native_total, aud_total FROM v_financial_year_summary;"
```
```bash
sqlite3 -header -column knowledge/projects/smsf/smsf.sqlite \
"SELECT transaction_id, trade_date, ticker, side, quantity, total_currency, total_cash_amount, total_cash_amount_aud, source_document FROM v_transactions_aud ORDER BY trade_date;"
```
```bash
sqlite3 -header -column knowledge/projects/smsf/smsf.sqlite \
"SELECT path, sha256, size_bytes FROM source_documents ORDER BY path;"
```
## Rebuild process
The DB can be rebuilt from current project files with:
```bash
# Make a backup first, then explicitly force the rebuild
knowledge/projects/smsf/scripts/build_smsf_db.py --force
```
The script refuses to overwrite an existing database unless `--force` is supplied. It does not delete or move original files. It rebuilds `smsf.sqlite`, indexes source documents, imports the CSV ledger, recreates lots/views, and regenerates verification reports.
## Audit posture
- Keep source documents immutable.
- Record corrections/assumptions in `decisions`, transaction `notes`, or future adjustment tables rather than silently changing evidence.
- Accountant/auditor should confirm final tax classifications, FX treatment, and reporting format.
+405
View File
@@ -0,0 +1,405 @@
#!/usr/bin/env python3
"""Build/rebuild the SMSF SQLite ledger from current canonical project files.
Safe behaviour:
- Does not delete or move source files.
- Rebuilds smsf.sqlite from CSV/source-documents so imports are repeatable.
- Stores hashes for source documents.
- Generates read-only reports from DB for verification.
"""
from __future__ import annotations
import argparse
import csv
import datetime as dt
import hashlib
import json
import re
import sqlite3
from decimal import Decimal, InvalidOperation
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
DB = ROOT / "smsf.sqlite"
CSV = ROOT / "transactions" / "actual-transactions.csv"
SOURCE_DOCS = ROOT / "source-documents"
REPORTS = ROOT / "reports"
def sha256(path: Path) -> str:
h = hashlib.sha256()
with path.open('rb') as f:
for chunk in iter(lambda: f.read(1024 * 1024), b''):
h.update(chunk)
return h.hexdigest()
def dec(v: str | None) -> str | None:
if v is None or str(v).strip() == "":
return None
try:
return str(Decimal(str(v).strip()))
except InvalidOperation:
return None
def text(v: str | None) -> str | None:
if v is None:
return None
v = v.strip()
return v if v else None
def infer_financial_year(date_s: str | None) -> str | None:
if not date_s:
return None
m = re.match(r"(\d{4})-(\d{2})-(\d{2})", date_s)
if not m:
return None
y, mo = int(m.group(1)), int(m.group(2))
return f"{y}-{(y+1)%100:02d}" if mo >= 7 else f"{y-1}-{y%100:02d}"
def parse_aud_equivalent(notes: str | None, currency: str | None, amount: str | None) -> str | None:
if currency == "AUD":
return dec(amount)
if not notes:
return None
m = re.search(r"AUD equivalent of total cash amount\s*=\s*A\$\s*([0-9,]+\.\d{2})", notes, re.I)
if not m:
m = re.search(r"AUD equivalent\s*(?:=|:)?\s*A\$\s*([0-9,]+\.\d{2})", notes, re.I)
return dec(m.group(1).replace(',', '')) if m else None
def parse_fx_rate(notes: str | None) -> str | None:
if not notes:
return None
m = re.search(r"US\$1\s*=\s*A\$\s*([0-9.]+)", notes)
return dec(m.group(1)) if m else None
def connect() -> sqlite3.Connection:
con = sqlite3.connect(DB)
con.execute("PRAGMA foreign_keys = ON")
con.row_factory = sqlite3.Row
return con
def create_schema(con: sqlite3.Connection) -> None:
con.executescript(
"""
DROP VIEW IF EXISTS v_holdings;
DROP VIEW IF EXISTS v_transactions_aud;
DROP VIEW IF EXISTS v_financial_year_summary;
DROP TABLE IF EXISTS audit_log;
DROP TABLE IF EXISTS decisions;
DROP TABLE IF EXISTS transaction_documents;
DROP TABLE IF EXISTS source_documents;
DROP TABLE IF EXISTS fx_rates;
DROP TABLE IF EXISTS cash_movements;
DROP TABLE IF EXISTS trade_lots;
DROP TABLE IF EXISTS transactions;
DROP TABLE IF EXISTS import_batches;
CREATE TABLE import_batches (
id INTEGER PRIMARY KEY AUTOINCREMENT,
imported_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
source_path TEXT NOT NULL,
source_sha256 TEXT,
row_count INTEGER NOT NULL,
notes TEXT
);
CREATE TABLE transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
transaction_id TEXT NOT NULL UNIQUE,
status TEXT NOT NULL,
asset_class TEXT,
instrument_type TEXT,
market TEXT,
ticker TEXT,
name TEXT,
side TEXT,
order_type TEXT,
trade_datetime TEXT,
timezone TEXT,
trade_date TEXT,
financial_year TEXT,
settlement_date TEXT,
quantity TEXT,
price_currency TEXT,
price TEXT,
gross_currency TEXT,
gross_amount TEXT,
brokerage_currency TEXT,
brokerage_and_gst TEXT,
total_currency TEXT,
total_cash_amount TEXT,
total_cash_amount_aud TEXT,
fx_rate_to_aud TEXT,
broker TEXT,
account TEXT,
source_document TEXT,
notes TEXT,
import_batch_id INTEGER REFERENCES import_batches(id),
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE trade_lots (
id INTEGER PRIMARY KEY AUTOINCREMENT,
transaction_id TEXT NOT NULL REFERENCES transactions(transaction_id) ON DELETE CASCADE,
lot_id TEXT NOT NULL UNIQUE,
asset_class TEXT,
ticker TEXT,
name TEXT,
acquisition_date TEXT,
quantity_acquired TEXT,
quantity_remaining TEXT,
native_currency TEXT,
native_cost_base TEXT,
aud_cost_base TEXT,
broker TEXT,
account TEXT,
notes TEXT
);
CREATE TABLE cash_movements (
id INTEGER PRIMARY KEY AUTOINCREMENT,
movement_id TEXT NOT NULL UNIQUE,
movement_date TEXT,
financial_year TEXT,
movement_type TEXT,
platform TEXT,
account TEXT,
currency TEXT,
amount TEXT,
amount_aud TEXT,
related_transaction_id TEXT REFERENCES transactions(transaction_id),
source_document TEXT,
notes TEXT
);
CREATE TABLE fx_rates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
fx_id TEXT NOT NULL UNIQUE,
rate_date TEXT,
from_currency TEXT NOT NULL,
to_currency TEXT NOT NULL,
rate TEXT NOT NULL,
source TEXT,
source_document TEXT,
notes TEXT
);
CREATE TABLE source_documents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
path TEXT NOT NULL UNIQUE,
doc_type TEXT,
sha256 TEXT NOT NULL,
size_bytes INTEGER NOT NULL,
captured_date TEXT,
description TEXT,
ocr_text TEXT,
notes TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE transaction_documents (
transaction_id TEXT NOT NULL REFERENCES transactions(transaction_id) ON DELETE CASCADE,
source_document_id INTEGER NOT NULL REFERENCES source_documents(id) ON DELETE CASCADE,
relationship TEXT NOT NULL DEFAULT 'evidence',
PRIMARY KEY (transaction_id, source_document_id, relationship)
);
CREATE TABLE decisions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
decision_date TEXT NOT NULL,
topic TEXT NOT NULL,
decision TEXT NOT NULL,
source TEXT,
notes TEXT
);
CREATE TABLE audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
event_time TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
event_type TEXT NOT NULL,
entity_type TEXT,
entity_id TEXT,
details TEXT
);
CREATE INDEX idx_transactions_ticker ON transactions(ticker);
CREATE INDEX idx_transactions_trade_date ON transactions(trade_date);
CREATE INDEX idx_transactions_financial_year ON transactions(financial_year);
CREATE INDEX idx_transactions_broker ON transactions(broker);
CREATE INDEX idx_lots_ticker ON trade_lots(ticker);
CREATE INDEX idx_docs_sha ON source_documents(sha256);
CREATE VIEW v_transactions_aud AS
SELECT transaction_id, status, financial_year, trade_date, trade_datetime, timezone,
asset_class, instrument_type, market, ticker, name, side, quantity,
price_currency, price, total_currency, total_cash_amount,
total_cash_amount_aud, fx_rate_to_aud, brokerage_and_gst,
broker, account, source_document, notes
FROM transactions;
CREATE VIEW v_holdings AS
SELECT ticker, name, asset_class, instrument_type, market, broker, account,
SUM(CASE WHEN lower(side)='buy' THEN CAST(quantity AS REAL)
WHEN lower(side)='sell' THEN -CAST(quantity AS REAL)
ELSE 0 END) AS quantity,
SUM(CASE WHEN lower(side)='buy' THEN CAST(COALESCE(total_cash_amount_aud,total_cash_amount) AS REAL)
WHEN lower(side)='sell' THEN -CAST(COALESCE(total_cash_amount_aud,total_cash_amount) AS REAL)
ELSE 0 END) AS aud_cost_base
FROM transactions
WHERE status='executed' AND ticker IS NOT NULL
GROUP BY ticker, name, asset_class, instrument_type, market, broker, account;
CREATE VIEW v_financial_year_summary AS
SELECT financial_year, asset_class, total_currency,
COUNT(*) AS transaction_count,
SUM(CAST(total_cash_amount AS REAL)) AS native_total,
SUM(CAST(COALESCE(total_cash_amount_aud,total_cash_amount) AS REAL)) AS aud_total
FROM transactions
GROUP BY financial_year, asset_class, total_currency;
"""
)
def import_source_documents(con: sqlite3.Connection) -> dict[str, int]:
mapping: dict[str, int] = {}
for path in sorted(SOURCE_DOCS.rglob('*')):
if not path.is_file():
continue
rel = path.relative_to(ROOT).as_posix()
ext = path.suffix.lower()
doc_type = {'.jpg': 'screenshot/image', '.jpeg': 'screenshot/image', '.png': 'screenshot/image', '.csv': 'csv/export', '.pdf': 'pdf'}.get(ext, ext.lstrip('.') or 'unknown')
cur = con.execute(
"INSERT INTO source_documents(path, doc_type, sha256, size_bytes, description) VALUES (?, ?, ?, ?, ?)",
(rel, doc_type, sha256(path), path.stat().st_size, path.parent.name.replace('-', ' ')),
)
mapping[rel] = int(cur.lastrowid)
mapping['../' + rel] = int(cur.lastrowid)
return mapping
def import_transactions(con: sqlite3.Connection, doc_map: dict[str, int]) -> int:
rows = list(csv.DictReader(CSV.open(newline='')))
batch = con.execute(
"INSERT INTO import_batches(source_path, source_sha256, row_count, notes) VALUES (?, ?, ?, ?)",
(CSV.relative_to(ROOT).as_posix(), sha256(CSV), len(rows), 'Imported from existing canonical CSV during SQLite migration.'),
).lastrowid
for r in rows:
trade_datetime = text(r.get('trade_datetime'))
trade_date = trade_datetime[:10] if trade_datetime else None
fy = infer_financial_year(trade_date)
total_cur = text(r.get('total_currency'))
total_amt = dec(r.get('total_cash_amount'))
notes = text(r.get('notes'))
aud_equiv = parse_aud_equivalent(notes, total_cur, total_amt)
fx_rate = parse_fx_rate(notes)
vals = (
text(r.get('transaction_id')), text(r.get('status')) or 'unknown', text(r.get('asset_class')),
text(r.get('instrument_type')), text(r.get('market')), text(r.get('ticker')), text(r.get('name')),
text(r.get('side')), text(r.get('order_type')), trade_datetime, text(r.get('timezone')), trade_date, fy,
text(r.get('settlement_date')), dec(r.get('quantity')), text(r.get('price_currency')), dec(r.get('price')),
text(r.get('gross_currency')), dec(r.get('gross_amount')), text(r.get('brokerage_currency')), dec(r.get('brokerage_and_gst')),
total_cur, total_amt, aud_equiv, fx_rate, text(r.get('broker')), text(r.get('account')),
text(r.get('source_document')), notes, batch
)
con.execute(
"""INSERT INTO transactions (
transaction_id,status,asset_class,instrument_type,market,ticker,name,side,order_type,trade_datetime,timezone,trade_date,financial_year,
settlement_date,quantity,price_currency,price,gross_currency,gross_amount,brokerage_currency,brokerage_and_gst,total_currency,total_cash_amount,
total_cash_amount_aud,fx_rate_to_aud,broker,account,source_document,notes,import_batch_id
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
vals,
)
source_doc = text(r.get('source_document'))
doc_id = doc_map.get(source_doc or '')
if doc_id:
con.execute("INSERT OR IGNORE INTO transaction_documents(transaction_id, source_document_id) VALUES (?, ?)", (text(r.get('transaction_id')), doc_id))
# For buys, create an open lot. Sells/disposals can be matched later.
if (text(r.get('status')) == 'executed') and (text(r.get('side')) or '').lower() == 'buy':
tid = text(r.get('transaction_id'))
con.execute(
"""INSERT INTO trade_lots(lot_id, transaction_id, asset_class, ticker, name, acquisition_date,
quantity_acquired, quantity_remaining, native_currency, native_cost_base, aud_cost_base, broker, account, notes)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(f"LOT-{tid}", tid, text(r.get('asset_class')), text(r.get('ticker')), text(r.get('name')), trade_date,
dec(r.get('quantity')), dec(r.get('quantity')), total_cur, total_amt, aud_equiv or (total_amt if total_cur == 'AUD' else None),
text(r.get('broker')), text(r.get('account')), 'Auto-created from executed buy transaction during SQLite migration.'),
)
return len(rows)
def seed_fx_and_cash(con: sqlite3.Connection, doc_map: dict[str, int]) -> None:
# Known Stake FX confirmation captured in existing notes.
con.execute(
"""INSERT INTO fx_rates(fx_id, rate_date, from_currency, to_currency, rate, source, source_document, notes)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
('2026-05-08-STAKE-USD-AUD-001', '2026-05-08', 'USD', 'AUD', '1.3813833726', 'Stake deposit confirmation',
'source-documents/2026-05-08-us-fx/stake-fx-deposit-confirmation.jpg', 'Inverse of A$1 = US$0.723912; received US$17,960.30 from A$25,000.00 with US$137.50 fees.'),
)
con.execute(
"""INSERT INTO cash_movements(movement_id, movement_date, financial_year, movement_type, platform, account, currency, amount, amount_aud, source_document, notes)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
('2026-05-08-STAKE-FX-DEPOSIT-001', '2026-05-08', infer_financial_year('2026-05-08'), 'fx_deposit', 'Stake', 'SMSF', 'USD', '17960.30', '25000.00',
'source-documents/2026-05-08-us-fx/stake-fx-deposit-confirmation.jpg', 'Stake confirmation: sent A$25,000.00, received US$17,960.30, fees US$137.50, reference 3B08FFB9.'),
)
def seed_decisions(con: sqlite3.Connection) -> None:
decisions = [
('2026-05-14', 'SMSF record keeping', 'Create SQLite database as canonical structured ledger while preserving existing CSV/Markdown/source documents.', 'Telegram discussion', 'No original files deleted or moved.'),
('2026-05-08', 'US trade FX rate', 'Use Stake deposit confirmation rate A$1 = US$0.723912 / US$1 = A$1.3813833726 for initial US purchases unless accountant advises otherwise.', 'transactions/fx-rate-notes.md', None),
('2026-05-08', 'BTC exchange', 'BTC order-history rows are recorded as CoinSpot SMSF BTC/AUD buys.', 'transactions/btc-orderhistory-summary.md', 'Broker/exchange confirmed by Michael.'),
]
con.executemany("INSERT INTO decisions(decision_date, topic, decision, source, notes) VALUES (?, ?, ?, ?, ?)", decisions)
def write_reports(con: sqlite3.Connection) -> None:
REPORTS.mkdir(exist_ok=True)
rows = con.execute("SELECT * FROM v_holdings ORDER BY asset_class, ticker").fetchall()
lines = ["# SMSF Holdings from SQLite", "", "Generated from `smsf.sqlite`.", "", "| Ticker | Name | Asset class | Market | Quantity | AUD cost base | Broker |", "|---|---|---|---|---:|---:|---|"]
for r in rows:
lines.append(f"| {r['ticker']} | {r['name']} | {r['asset_class']} | {r['market']} | {r['quantity']:.11g} | {r['aud_cost_base']:.2f} | {r['broker']} |")
(REPORTS / "holdings-from-db.md").write_text("\n".join(lines) + "\n")
tx_count = con.execute("SELECT COUNT(*) FROM transactions").fetchone()[0]
doc_count = con.execute("SELECT COUNT(*) FROM source_documents").fetchone()[0]
lot_count = con.execute("SELECT COUNT(*) FROM trade_lots").fetchone()[0]
sums = con.execute("SELECT total_currency, COUNT(*) c, SUM(CAST(total_cash_amount AS REAL)) total FROM transactions GROUP BY total_currency ORDER BY total_currency").fetchall()
aud_sum = con.execute("SELECT SUM(CAST(COALESCE(total_cash_amount_aud,total_cash_amount) AS REAL)) FROM transactions").fetchone()[0]
vlines = ["# SMSF SQLite Migration Verification", "", f"Generated: {dt.datetime.now().astimezone().isoformat(timespec='seconds')}", "", "## Counts", "", f"- Transactions imported: **{tx_count}**", f"- Trade lots created: **{lot_count}**", f"- Source documents indexed: **{doc_count}**", "", "## Native totals by currency", ""]
for s in sums:
vlines.append(f"- {s['total_currency']}: {s['c']} transaction(s), total {s['total']:.2f}")
vlines += ["", f"## AUD reporting total where known", "", f"- Total AUD-equivalent transaction cash amount: **A${aud_sum:.2f}**", "", "## Notes", "", "- Existing source documents, Markdown files, CSV ledger, and workbook were not deleted or moved.", "- This database is record-keeping support only; accountant/auditor should confirm final classifications and reporting treatment."]
(REPORTS / "sqlite-migration-verification.md").write_text("\n".join(vlines) + "\n")
def main() -> None:
ap = argparse.ArgumentParser(description='Build/rebuild the SMSF SQLite ledger from current project files')
ap.add_argument('--force', action='store_true', help='Allow rebuilding an existing smsf.sqlite database')
args = ap.parse_args()
if DB.exists() and not args.force:
raise SystemExit(f"Refusing to rebuild existing {DB}. Use --force after making a backup.")
with connect() as con:
create_schema(con)
doc_map = import_source_documents(con)
count = import_transactions(con, doc_map)
seed_fx_and_cash(con, doc_map)
seed_decisions(con)
con.execute("INSERT INTO audit_log(event_type, entity_type, entity_id, details) VALUES (?, ?, ?, ?)", ('migration', 'database', 'smsf.sqlite', json.dumps({'transactions_imported': count, 'source_documents_indexed': len(doc_map)})))
con.commit()
write_reports(con)
print(f"Built {DB}")
if __name__ == '__main__':
main()
+166
View File
@@ -0,0 +1,166 @@
CREATE TABLE import_batches (
id INTEGER PRIMARY KEY AUTOINCREMENT,
imported_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
source_path TEXT NOT NULL,
source_sha256 TEXT,
row_count INTEGER NOT NULL,
notes TEXT
);
CREATE TABLE sqlite_sequence(name,seq);
CREATE TABLE transactions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
transaction_id TEXT NOT NULL UNIQUE,
status TEXT NOT NULL,
asset_class TEXT,
instrument_type TEXT,
market TEXT,
ticker TEXT,
name TEXT,
side TEXT,
order_type TEXT,
trade_datetime TEXT,
timezone TEXT,
trade_date TEXT,
financial_year TEXT,
settlement_date TEXT,
quantity TEXT,
price_currency TEXT,
price TEXT,
gross_currency TEXT,
gross_amount TEXT,
brokerage_currency TEXT,
brokerage_and_gst TEXT,
total_currency TEXT,
total_cash_amount TEXT,
total_cash_amount_aud TEXT,
fx_rate_to_aud TEXT,
broker TEXT,
account TEXT,
source_document TEXT,
notes TEXT,
import_batch_id INTEGER REFERENCES import_batches(id),
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE trade_lots (
id INTEGER PRIMARY KEY AUTOINCREMENT,
transaction_id TEXT NOT NULL REFERENCES transactions(transaction_id) ON DELETE CASCADE,
lot_id TEXT NOT NULL UNIQUE,
asset_class TEXT,
ticker TEXT,
name TEXT,
acquisition_date TEXT,
quantity_acquired TEXT,
quantity_remaining TEXT,
native_currency TEXT,
native_cost_base TEXT,
aud_cost_base TEXT,
broker TEXT,
account TEXT,
notes TEXT
);
CREATE TABLE cash_movements (
id INTEGER PRIMARY KEY AUTOINCREMENT,
movement_id TEXT NOT NULL UNIQUE,
movement_date TEXT,
financial_year TEXT,
movement_type TEXT,
platform TEXT,
account TEXT,
currency TEXT,
amount TEXT,
amount_aud TEXT,
related_transaction_id TEXT REFERENCES transactions(transaction_id),
source_document TEXT,
notes TEXT
);
CREATE TABLE fx_rates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
fx_id TEXT NOT NULL UNIQUE,
rate_date TEXT,
from_currency TEXT NOT NULL,
to_currency TEXT NOT NULL,
rate TEXT NOT NULL,
source TEXT,
source_document TEXT,
notes TEXT
);
CREATE TABLE source_documents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
path TEXT NOT NULL UNIQUE,
doc_type TEXT,
sha256 TEXT NOT NULL,
size_bytes INTEGER NOT NULL,
captured_date TEXT,
description TEXT,
ocr_text TEXT,
notes TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE transaction_documents (
transaction_id TEXT NOT NULL REFERENCES transactions(transaction_id) ON DELETE CASCADE,
source_document_id INTEGER NOT NULL REFERENCES source_documents(id) ON DELETE CASCADE,
relationship TEXT NOT NULL DEFAULT 'evidence',
PRIMARY KEY (transaction_id, source_document_id, relationship)
);
CREATE TABLE decisions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
decision_date TEXT NOT NULL,
topic TEXT NOT NULL,
decision TEXT NOT NULL,
source TEXT,
notes TEXT
);
CREATE TABLE audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
event_time TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
event_type TEXT NOT NULL,
entity_type TEXT,
entity_id TEXT,
details TEXT
);
CREATE INDEX idx_transactions_ticker ON transactions(ticker);
CREATE INDEX idx_transactions_trade_date ON transactions(trade_date);
CREATE INDEX idx_transactions_financial_year ON transactions(financial_year);
CREATE INDEX idx_transactions_broker ON transactions(broker);
CREATE INDEX idx_lots_ticker ON trade_lots(ticker);
CREATE INDEX idx_docs_sha ON source_documents(sha256);
CREATE VIEW v_transactions_aud AS
SELECT transaction_id, status, financial_year, trade_date, trade_datetime, timezone,
asset_class, instrument_type, market, ticker, name, side, quantity,
price_currency, price, total_currency, total_cash_amount,
total_cash_amount_aud, fx_rate_to_aud, brokerage_and_gst,
broker, account, source_document, notes
FROM transactions
/* v_transactions_aud(transaction_id,status,financial_year,trade_date,trade_datetime,timezone,asset_class,instrument_type,market,ticker,name,side,quantity,price_currency,price,total_currency,total_cash_amount,total_cash_amount_aud,fx_rate_to_aud,brokerage_and_gst,broker,account,source_document,notes) */;
CREATE VIEW v_holdings AS
SELECT ticker, name, asset_class, instrument_type, market, broker, account,
SUM(CASE WHEN lower(side)='buy' THEN CAST(quantity AS REAL)
WHEN lower(side)='sell' THEN -CAST(quantity AS REAL)
ELSE 0 END) AS quantity,
SUM(CASE WHEN lower(side)='buy' THEN
CASE WHEN total_cash_amount_aud IS NOT NULL THEN CAST(total_cash_amount_aud AS REAL)
WHEN total_currency='AUD' THEN CAST(total_cash_amount AS REAL)
ELSE NULL END
WHEN lower(side)='sell' THEN
CASE WHEN total_cash_amount_aud IS NOT NULL THEN -CAST(total_cash_amount_aud AS REAL)
WHEN total_currency='AUD' THEN -CAST(total_cash_amount AS REAL)
ELSE NULL END
ELSE 0 END) AS aud_cost_base
FROM transactions
WHERE status='executed' AND ticker IS NOT NULL
GROUP BY ticker, name, asset_class, instrument_type, market, broker, account
/* v_holdings(ticker,name,asset_class,instrument_type,market,broker,account,quantity,aud_cost_base) */;
CREATE VIEW v_financial_year_summary AS
SELECT financial_year, asset_class, total_currency,
COUNT(*) AS transaction_count,
SUM(CAST(total_cash_amount AS REAL)) AS native_total,
CASE WHEN SUM(CASE WHEN total_currency!='AUD' AND total_cash_amount_aud IS NULL THEN 1 ELSE 0 END) > 0
THEN NULL
ELSE SUM(CASE WHEN total_cash_amount_aud IS NOT NULL THEN CAST(total_cash_amount_aud AS REAL)
WHEN total_currency='AUD' THEN CAST(total_cash_amount AS REAL)
ELSE NULL END)
END AS aud_total
FROM transactions
GROUP BY financial_year, asset_class, total_currency
/* v_financial_year_summary(financial_year,asset_class,total_currency,transaction_count,native_total,aud_total) */;
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

@@ -1,6 +1,6 @@
# SMSF Actual Holdings Register
Purpose: canonical human-readable summary of real executed SMSF investments. Use `actual-transactions.csv` as the analyzable ledger for reporting/tax work.
Purpose: canonical human-readable summary of real executed SMSF investments. Use `smsf.sqlite` as the canonical structured ledger for reporting/tax work; `actual-transactions.csv` is legacy/import seed material.
## Current confirmed holdings
@@ -9,6 +9,7 @@ Purpose: canonical human-readable summary of real executed SMSF investments. Use
| Bitcoin | BTC | Cryptocurrency | BTC/AUD | 1.37685299877 | A$108,936.96/BTC incl fees | A$149,990.18 | CoinSpot | 2026-04-23 | `source-documents/2026-05-08-btc-orderhistory/orderhistory.csv` |
| BetaShares NASDAQ 100 ETF | NDQ | ETF | ASX | 429 | A$58.16 | A$24,953.64 | Stake | 2026-05-07 | `source-documents/2026-05-07-ndq-buy/stake-ndq-buy-confirmation.jpg` |
| Alphabet Inc Class A | GOOGL | Share | NASDAQ | 12 | US$396.20 | US$4,757.40 / A$6,571.79 | Stake | 2026-05-07 | `source-documents/2026-05-08-us-buys/purchase-1.jpg` |
| Amazon.com Inc | AMZN | Share | NASDAQ | 18 | US$265.50 | US$4,782.00 / A$6,605.78 | Stake | 2026-05-21 | `source-documents/2026-05-21-amzn-buy/stake-amzn-buy-screenshot.jpg` |
| NVIDIA Corp | NVDA | Share | NASDAQ | 22 | US$211.50 | US$4,656.00 / A$6,431.72 | Stake | 2026-05-07 | `source-documents/2026-05-08-us-buys/purchase-3.jpg` |
| Tesla Inc | TSLA | Share | NASDAQ | 8 | US$408.00 | US$3,267.00 / A$4,512.98 | Stake | 2026-05-07 | `source-documents/2026-05-08-us-buys/purchase-2.jpg` |
@@ -33,7 +34,11 @@ Purpose: canonical human-readable summary of real executed SMSF investments. Use
- GOOGL: 12 shares, limit buy at US$396.20, gross US$4,754.40 + US$3.00 brokerage = US$4,757.40 total; screenshot showed 07 May 2026, 5:31pm NY.
- NVDA: 22 shares, limit buy at US$211.50, gross US$4,653.00 + US$3.00 brokerage = US$4,656.00 total; screenshot showed 07 May 2026, 5:39pm NY.
- TSLA: 8 shares, limit buy at US$408.00, gross US$3,264.00 + US$3.00 brokerage = US$3,267.00 total; screenshot showed 07 May 2026, 5:44pm NY.
- FX/AUD conversion for the US purchases uses the Stake deposit confirmation supplied 2026-05-08: **A$1 = US$0.723912**, equivalent to **US$1 = A$1.3813833726**; see `transactions/fx-rate-notes.md` and source document `source-documents/2026-05-08-us-fx/stake-fx-deposit-confirmation.jpg`.
- AMZN purchase recorded from screenshot supplied 2026-05-22:
- AMZN: 18 shares, limit buy at US$265.50, gross US$4,779.00 + US$3.00 brokerage + US$0.00 regulatory fees = US$4,782.00 total; screenshot showed 21 May 2026, 6:25am NY.
- Trade confirmation noted as available T+1. Michael confirmed this used leftover USD from the original Stake transfer into the trading account, so the same captured FX rate is applied: US$4,782.00 × 1.3813833726 = **A$6,605.78**.
- FX/AUD conversion for the 2026-05-08 US purchase batch and the later AMZN purchase uses the Stake deposit confirmation supplied 2026-05-08 where applicable: **A$1 = US$0.723912**, equivalent to **US$1 = A$1.3813833726**; see `transactions/fx-rate-notes.md` and source document `source-documents/2026-05-08-us-fx/stake-fx-deposit-confirmation.jpg`.
- Deposit confirmation also showed **US$137.50 total fees**. Those fees are noted separately and have not been allocated across individual holdings unless accountant treatment later requires it.
- This is record-keeping support only; final tax/accounting treatment should be checked with the SMSF accountant/auditor.