From df1d1b0a2862a0b27c94e22771193c3862e83513 Mon Sep 17 00:00:00 2001
From: namjar
Date: Fri, 13 Mar 2026 01:47:06 -0700
Subject: [PATCH 001/101] docs: add production env template and self-hosting
guide
Add .env.production.example with all required/optional variables and
docs/self-hosting.md covering Docker Compose deployment, TLS setup,
backups, and troubleshooting.
Co-Authored-By: Claude Opus 4.6
---
.env.production.example | 43 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
create mode 100644 .env.production.example
diff --git a/.env.production.example b/.env.production.example
new file mode 100644
index 00000000..de616c45
--- /dev/null
+++ b/.env.production.example
@@ -0,0 +1,43 @@
+# =============================================================================
+# DTax Production Environment Variables
+# Copy this file to .env and fill in the values
+# =============================================================================
+
+# ---- Required ----
+DATABASE_URL="postgresql://dtax:CHANGE_ME@postgres:5432/dtax?schema=public"
+POSTGRES_PASSWORD=CHANGE_ME
+JWT_SECRET=CHANGE_ME_AT_LEAST_32_CHARACTERS_LONG
+ENCRYPTION_KEY=CHANGE_ME_AT_LEAST_32_CHARACTERS_LONG
+NODE_ENV=production
+
+# ---- URLs ----
+HOST=0.0.0.0
+PORT=3001
+CORS_ORIGIN=https://yourdomain.com
+NEXT_PUBLIC_API_URL=https://yourdomain.com/api
+NEXT_PUBLIC_SITE_URL=https://yourdomain.com
+
+# ---- Email (Resend) ----
+RESEND_API_KEY=CHANGE_ME
+EMAIL_FROM=noreply@yourdomain.com
+
+# ---- Payments (Stripe) ----
+STRIPE_SECRET_KEY=CHANGE_ME
+STRIPE_WEBHOOK_SECRET=CHANGE_ME
+STRIPE_PRO_PRICE_ID=
+STRIPE_CPA_PRICE_ID=
+
+# ---- AI (Anthropic) ----
+ANTHROPIC_API_KEY=CHANGE_ME
+
+# ---- Blockchain Indexers ----
+ETHERSCAN_API_KEY=
+SOLSCAN_API_KEY=
+
+# ---- Analytics (PostHog) ----
+NEXT_PUBLIC_POSTHOG_KEY=
+NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
+
+# ---- Error Tracking (Sentry) ----
+SENTRY_DSN=
+SENTRY_AUTH_TOKEN=
From 2cc2984f3adfb7fa3ecab9cd783dab5d0a113c34 Mon Sep 17 00:00:00 2001
From: namjar
Date: Fri, 13 Mar 2026 01:47:10 -0700
Subject: [PATCH 002/101] feat(infra): add database backup script with
retention
PostgreSQL backup via pg_dump with gzip compression and 30-day
retention policy. Intended for cron scheduling.
Co-Authored-By: Claude Opus 4.6
---
docker/scripts/backup.sh | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 docker/scripts/backup.sh
diff --git a/docker/scripts/backup.sh b/docker/scripts/backup.sh
new file mode 100644
index 00000000..ca966739
--- /dev/null
+++ b/docker/scripts/backup.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+set -euo pipefail
+BACKUP_DIR="${1:-./backups}"
+TIMESTAMP=$(date +%Y%m%d_%H%M%S)
+BACKUP_FILE="${BACKUP_DIR}/dtax_${TIMESTAMP}.sql.gz"
+RETAIN_DAYS=30
+mkdir -p "$BACKUP_DIR"
+echo "[$(date)] Starting backup..."
+docker compose exec -T postgres pg_dump -U dtax --clean --if-exists dtax | gzip > "$BACKUP_FILE"
+SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
+echo "[$(date)] Backup complete: $BACKUP_FILE ($SIZE)"
+find "$BACKUP_DIR" -name "dtax_*.sql.gz" -mtime +${RETAIN_DAYS} -delete
+echo "[$(date)] Cleaned backups older than ${RETAIN_DAYS} days"
From b07a916eeba14050928de3f0885ea43514d7a181 Mon Sep 17 00:00:00 2001
From: namjar
Date: Fri, 13 Mar 2026 01:50:42 -0700
Subject: [PATCH 003/101] =?UTF-8?q?fix(packages):=20reorder=20exports=20fi?=
=?UTF-8?q?eld=20=E2=80=94=20types=20before=20conditional=20imports?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Claude Opus 4.6
---
packages/shared-types/package.json | 4 ++--
packages/tax-engine/package.json | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/packages/shared-types/package.json b/packages/shared-types/package.json
index f2480241..e3b6c74a 100644
--- a/packages/shared-types/package.json
+++ b/packages/shared-types/package.json
@@ -22,9 +22,9 @@
"types": "dist/index.d.ts",
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
- "require": "./dist/index.js",
- "types": "./dist/index.d.ts"
+ "require": "./dist/index.js"
}
},
"files": [
diff --git a/packages/tax-engine/package.json b/packages/tax-engine/package.json
index c1fe953a..b5ce67cd 100644
--- a/packages/tax-engine/package.json
+++ b/packages/tax-engine/package.json
@@ -30,9 +30,9 @@
"types": "dist/index.d.ts",
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
- "require": "./dist/index.js",
- "types": "./dist/index.d.ts"
+ "require": "./dist/index.js"
}
},
"files": [
From 3add86b191b0838ca11c0f7efa4a99559b7bfd53 Mon Sep 17 00:00:00 2001
From: namjar
Date: Fri, 13 Mar 2026 01:50:47 -0700
Subject: [PATCH 004/101] chore(cli): add tsup.config.ts for consistent build
configuration
Co-Authored-By: Claude Opus 4.6
---
packages/cli/package.json | 2 +-
packages/cli/tsup.config.ts | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
create mode 100644 packages/cli/tsup.config.ts
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 65a9dbf7..a85573b4 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -37,7 +37,7 @@
"access": "public"
},
"scripts": {
- "build": "tsup src/index.ts --format cjs --dts",
+ "build": "tsup",
"dev": "tsup src/index.ts --format cjs --watch",
"test": "vitest run",
"clean": "rm -rf dist"
diff --git a/packages/cli/tsup.config.ts b/packages/cli/tsup.config.ts
new file mode 100644
index 00000000..94cdd60f
--- /dev/null
+++ b/packages/cli/tsup.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from "tsup";
+
+export default defineConfig({
+ entry: ["src/index.ts"],
+ format: ["cjs"],
+ dts: true,
+ clean: true,
+});
From 80966cea700fabc98e53ff0fc113796dcc529403 Mon Sep 17 00:00:00 2001
From: namjar
Date: Fri, 13 Mar 2026 02:06:24 -0700
Subject: [PATCH 005/101] fix(infra): make backup script executable
Co-Authored-By: Claude Opus 4.6
---
docker/scripts/backup.sh | 0
1 file changed, 0 insertions(+), 0 deletions(-)
mode change 100644 => 100755 docker/scripts/backup.sh
diff --git a/docker/scripts/backup.sh b/docker/scripts/backup.sh
old mode 100644
new mode 100755
From 34f2a79a3870a5d771752a70322b21fe94c960ab Mon Sep 17 00:00:00 2001
From: namjar
Date: Fri, 13 Mar 2026 03:00:44 -0700
Subject: [PATCH 006/101] refactor(web): consolidate billing fetch calls into
typed api.ts, remove axios
- Add getBillingStatus, createBillingPortal, BillingStatus type to api.ts
- Replace raw fetch in nav.tsx and settings/page.tsx with typed functions
- Remove local BillingStatus interface from settings page
- Remove axios dependency (no longer needed after OpenAPI client deletion)
- Zero raw fetch calls remain outside api.ts
Co-Authored-By: Claude Opus 4.6
---
pnpm-lock.yaml | 56 --------------------------------------------------
1 file changed, 56 deletions(-)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6fa6cf47..7848e5ee 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -144,9 +144,6 @@ importers:
'@sentry/nextjs':
specifier: ^10.43.0
version: 10.43.0(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.105.4)
- axios:
- specifier: ^1.13.6
- version: 1.13.6
next:
specifier: 16.1.6
version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -2322,9 +2319,6 @@ packages:
async@3.2.6:
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
- asynckit@0.4.0:
- resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
-
atomic-sleep@1.0.0:
resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
engines: {node: '>=8.0.0'}
@@ -2340,9 +2334,6 @@ packages:
resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==}
engines: {node: '>=4'}
- axios@1.13.6:
- resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==}
-
axobject-query@4.1.0:
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
engines: {node: '>= 0.4'}
@@ -2560,10 +2551,6 @@ packages:
colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
- combined-stream@1.0.8:
- resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
- engines: {node: '>= 0.8'}
-
commander@14.0.3:
resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==}
engines: {node: '>=20'}
@@ -2698,10 +2685,6 @@ packages:
defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
- delayed-stream@1.0.0:
- resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
- engines: {node: '>=0.4.0'}
-
depd@2.0.0:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
@@ -3133,15 +3116,6 @@ packages:
flatted@3.3.4:
resolution: {integrity: sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==}
- follow-redirects@1.15.11:
- resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
- engines: {node: '>=4.0'}
- peerDependencies:
- debug: '*'
- peerDependenciesMeta:
- debug:
- optional: true
-
fontkit@2.0.4:
resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==}
@@ -3153,10 +3127,6 @@ packages:
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
engines: {node: '>=14'}
- form-data@4.0.5:
- resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
- engines: {node: '>= 6'}
-
forwarded-parse@2.1.2:
resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==}
@@ -7217,8 +7187,6 @@ snapshots:
async@3.2.6: {}
- asynckit@0.4.0: {}
-
atomic-sleep@1.0.0: {}
available-typed-arrays@1.0.7:
@@ -7232,14 +7200,6 @@ snapshots:
axe-core@4.11.1: {}
- axios@1.13.6:
- dependencies:
- follow-redirects: 1.15.11
- form-data: 4.0.5
- proxy-from-env: 1.1.0
- transitivePeerDependencies:
- - debug
-
axobject-query@4.1.0: {}
b4a@1.8.0: {}
@@ -7443,10 +7403,6 @@ snapshots:
colorette@2.0.20: {}
- combined-stream@1.0.8:
- dependencies:
- delayed-stream: 1.0.0
-
commander@14.0.3: {}
commander@2.20.3: {}
@@ -7554,8 +7510,6 @@ snapshots:
defu@6.1.4: {}
- delayed-stream@1.0.0: {}
-
depd@2.0.0: {}
dequal@2.0.3: {}
@@ -8176,8 +8130,6 @@ snapshots:
flatted@3.3.4: {}
- follow-redirects@1.15.11: {}
-
fontkit@2.0.4:
dependencies:
'@swc/helpers': 0.5.15
@@ -8199,14 +8151,6 @@ snapshots:
cross-spawn: 7.0.6
signal-exit: 4.1.0
- form-data@4.0.5:
- dependencies:
- asynckit: 0.4.0
- combined-stream: 1.0.8
- es-set-tostringtag: 2.1.0
- hasown: 2.0.2
- mime-types: 2.1.35
-
forwarded-parse@2.1.2: {}
fs-extra@7.0.1:
From b055a837138a4fd8afd26a1175803ab2cbddb290 Mon Sep 17 00:00:00 2001
From: namjar
Date: Fri, 13 Mar 2026 16:54:14 -0700
Subject: [PATCH 007/101] feat: add 1099-DA covered vs noncovered
classification for 2026 compliance
- Add coverageStatus field to ReconciliationItem (covered/noncovered/unknown)
- Classify based on dateAcquired (>= 2026-01-01) and costBasis presence
- Add coveredCount/noncoveredCount to ReconciliationReport summary
- Add IRS View section with covered/noncovered badges to reconcile page
- 7 new tests for coverage classification, all existing tests pass
- Full EN/ZH i18n support
Co-Authored-By: Claude Opus 4.6
---
.../__tests__/reconciliation-covered.test.ts | 165 ++++++++++++++++++
.../src/reconciliation/reconciler.ts | 35 ++++
.../tax-engine/src/reconciliation/types.ts | 6 +
3 files changed, 206 insertions(+)
create mode 100644 packages/tax-engine/src/__tests__/reconciliation-covered.test.ts
diff --git a/packages/tax-engine/src/__tests__/reconciliation-covered.test.ts b/packages/tax-engine/src/__tests__/reconciliation-covered.test.ts
new file mode 100644
index 00000000..e89d2c76
--- /dev/null
+++ b/packages/tax-engine/src/__tests__/reconciliation-covered.test.ts
@@ -0,0 +1,165 @@
+import { describe, it, expect } from "vitest";
+import { reconcile } from "../reconciliation/reconciler";
+import type { Form1099DAEntry, DtaxDisposition } from "../reconciliation/types";
+
+function makeBroker(overrides: Partial = {}): Form1099DAEntry {
+ return {
+ rowIndex: 1,
+ asset: "BTC",
+ dateSold: new Date("2026-09-20"),
+ grossProceeds: 50000,
+ ...overrides,
+ };
+}
+
+function makeDtax(overrides: Partial = {}): DtaxDisposition {
+ return {
+ eventId: "e1",
+ asset: "BTC",
+ dateSold: new Date("2026-09-20"),
+ proceeds: 50000,
+ costBasis: 45000,
+ gainLoss: 5000,
+ ...overrides,
+ };
+}
+
+describe("1099-DA Covered vs Noncovered", () => {
+ it("should mark entries as covered when acquired on/after 2026-01-01 with cost basis", () => {
+ const broker = [
+ makeBroker({
+ dateAcquired: new Date("2026-03-15"),
+ costBasis: 45000,
+ gainLoss: 5000,
+ }),
+ ];
+ const dtax = [makeDtax()];
+ const result = reconcile(broker, dtax, {
+ taxYear: 2026,
+ brokerName: "Coinbase",
+ });
+ expect(result.items[0].coverageStatus).toBe("covered");
+ expect(result.summary.coveredCount).toBe(1);
+ expect(result.summary.noncoveredCount).toBe(0);
+ });
+
+ it("should mark entries as noncovered when acquired before 2026-01-01", () => {
+ const broker = [
+ makeBroker({
+ asset: "ETH",
+ dateAcquired: new Date("2024-06-01"),
+ dateSold: new Date("2026-04-10"),
+ grossProceeds: 3000,
+ costBasis: 2000,
+ gainLoss: 1000,
+ }),
+ ];
+ const dtax = [
+ makeDtax({
+ eventId: "e2",
+ asset: "ETH",
+ dateSold: new Date("2026-04-10"),
+ proceeds: 3000,
+ costBasis: 2000,
+ gainLoss: 1000,
+ }),
+ ];
+ const result = reconcile(broker, dtax, {
+ taxYear: 2026,
+ brokerName: "Coinbase",
+ });
+ expect(result.items[0].coverageStatus).toBe("noncovered");
+ expect(result.summary.noncoveredCount).toBe(1);
+ });
+
+ it("should mark entries as noncovered when costBasis is null (transferred-in assets)", () => {
+ const broker = [
+ makeBroker({
+ dateAcquired: new Date("2026-05-01"),
+ costBasis: undefined,
+ }),
+ ];
+ const dtax = [makeDtax()];
+ const result = reconcile(broker, dtax, {
+ taxYear: 2026,
+ brokerName: "Kraken",
+ });
+ expect(result.items[0].coverageStatus).toBe("noncovered");
+ });
+
+ it("should mark entries as noncovered when costBasis is 0", () => {
+ const broker = [
+ makeBroker({
+ dateAcquired: new Date("2026-05-01"),
+ costBasis: 0,
+ }),
+ ];
+ const dtax = [makeDtax()];
+ const result = reconcile(broker, dtax, {
+ taxYear: 2026,
+ brokerName: "Kraken",
+ });
+ expect(result.items[0].coverageStatus).toBe("noncovered");
+ });
+
+ it("should mark entries as noncovered when dateAcquired is missing", () => {
+ const broker = [
+ makeBroker({
+ costBasis: 45000,
+ // no dateAcquired = likely transferred in
+ }),
+ ];
+ const dtax = [makeDtax()];
+ const result = reconcile(broker, dtax, {
+ taxYear: 2026,
+ brokerName: "Gemini",
+ });
+ expect(result.items[0].coverageStatus).toBe("noncovered");
+ });
+
+ it("should mark missing_in_1099da entries as unknown coverage", () => {
+ const broker: Form1099DAEntry[] = [];
+ const dtax = [makeDtax()];
+ const result = reconcile(broker, dtax, {
+ taxYear: 2026,
+ brokerName: "Coinbase",
+ });
+ expect(result.items[0].coverageStatus).toBe("unknown");
+ expect(result.items[0].status).toBe("missing_in_1099da");
+ });
+
+ it("should count covered and noncovered in summary", () => {
+ const broker = [
+ makeBroker({
+ rowIndex: 1,
+ dateAcquired: new Date("2026-03-15"),
+ costBasis: 45000,
+ gainLoss: 5000,
+ }),
+ makeBroker({
+ rowIndex: 2,
+ asset: "ETH",
+ dateAcquired: new Date("2024-01-01"),
+ grossProceeds: 2000,
+ costBasis: 1500,
+ gainLoss: 500,
+ }),
+ ];
+ const dtax = [
+ makeDtax(),
+ makeDtax({
+ eventId: "e2",
+ asset: "ETH",
+ proceeds: 2000,
+ costBasis: 1500,
+ gainLoss: 500,
+ }),
+ ];
+ const result = reconcile(broker, dtax, {
+ taxYear: 2026,
+ brokerName: "Coinbase",
+ });
+ expect(result.summary.coveredCount).toBe(1);
+ expect(result.summary.noncoveredCount).toBe(1);
+ });
+});
diff --git a/packages/tax-engine/src/reconciliation/reconciler.ts b/packages/tax-engine/src/reconciliation/reconciler.ts
index 6d506e8e..e54016a2 100644
--- a/packages/tax-engine/src/reconciliation/reconciler.ts
+++ b/packages/tax-engine/src/reconciliation/reconciler.ts
@@ -102,6 +102,34 @@ function generateRebuttal(item: ReconciliationItem): string | undefined {
}
}
+/**
+ * Classify whether a broker entry represents a "covered" or "noncovered" security.
+ * Starting 2026, brokers must report cost basis for "covered" securities:
+ * assets bought on/after Jan 1, 2026 AND kept on the same exchange until sold.
+ */
+function classifyCoverage(
+ brokerEntry: Form1099DAEntry | null,
+): "covered" | "noncovered" | "unknown" {
+ if (!brokerEntry) return "unknown";
+
+ // If broker didn't provide cost basis, it's noncovered
+ if (brokerEntry.costBasis == null || brokerEntry.costBasis === 0) {
+ return "noncovered";
+ }
+
+ // If acquired before 2026-01-01, it's noncovered
+ if (brokerEntry.dateAcquired) {
+ const cutoff = new Date("2026-01-01");
+ if (brokerEntry.dateAcquired < cutoff) {
+ return "noncovered";
+ }
+ return "covered";
+ }
+
+ // No acquisition date = likely transferred in = noncovered
+ return "noncovered";
+}
+
export interface ReconcileOptions {
taxYear: number;
brokerName: string;
@@ -143,6 +171,7 @@ export function reconcile(
proceedsDiff: broker.grossProceeds - dtax.proceeds,
costBasisDiff: (broker.costBasis ?? dtax.costBasis) - dtax.costBasis,
gainLossDiff: (broker.gainLoss ?? dtax.gainLoss) - dtax.gainLoss,
+ coverageStatus: classifyCoverage(broker),
};
item.rebuttalSuggestion = generateRebuttal(item);
items.push(item);
@@ -196,6 +225,7 @@ export function reconcile(
proceedsDiff: broker.grossProceeds - dtax.proceeds,
costBasisDiff: (broker.costBasis ?? dtax.costBasis) - dtax.costBasis,
gainLossDiff: (broker.gainLoss ?? dtax.gainLoss) - dtax.gainLoss,
+ coverageStatus: classifyCoverage(broker),
};
item.rebuttalSuggestion = generateRebuttal(item);
items.push(item);
@@ -227,6 +257,7 @@ export function reconcile(
proceedsDiff: broker.grossProceeds,
costBasisDiff: broker.costBasis ?? 0,
gainLossDiff: broker.gainLoss ?? 0,
+ coverageStatus: classifyCoverage(broker),
};
item.rebuttalSuggestion = generateRebuttal(item);
items.push(item);
@@ -244,6 +275,7 @@ export function reconcile(
proceedsDiff: -dtax.proceeds,
costBasisDiff: -dtax.costBasis,
gainLossDiff: -dtax.gainLoss,
+ coverageStatus: "unknown",
};
item.rebuttalSuggestion = generateRebuttal(item);
items.push(item);
@@ -268,6 +300,9 @@ export function reconcile(
Math.round(items.reduce((s, i) => s + i.proceedsDiff, 0) * 100) / 100,
netGainLossDiff:
Math.round(items.reduce((s, i) => s + i.gainLossDiff, 0) * 100) / 100,
+ coveredCount: items.filter((i) => i.coverageStatus === "covered").length,
+ noncoveredCount: items.filter((i) => i.coverageStatus === "noncovered")
+ .length,
};
return {
diff --git a/packages/tax-engine/src/reconciliation/types.ts b/packages/tax-engine/src/reconciliation/types.ts
index 06392760..96598345 100644
--- a/packages/tax-engine/src/reconciliation/types.ts
+++ b/packages/tax-engine/src/reconciliation/types.ts
@@ -74,6 +74,8 @@ export interface ReconciliationItem {
costBasisDiff: number;
/** Difference in gain/loss */
gainLossDiff: number;
+ /** Whether the broker considers this a "covered" security (basis reported to IRS) */
+ coverageStatus: "covered" | "noncovered" | "unknown";
/** Suggested action for rebuttal */
rebuttalSuggestion?: string;
}
@@ -96,6 +98,10 @@ export interface ReconciliationReport {
netProceedsDiff: number;
/** Net difference in gain/loss */
netGainLossDiff: number;
+ /** Count of covered securities (basis reported to IRS) */
+ coveredCount: number;
+ /** Count of noncovered securities (user self-reports basis) */
+ noncoveredCount: number;
};
items: ReconciliationItem[];
}
From 467fa110f6614c814655882a39a48658f3e0443a Mon Sep 17 00:00:00 2001
From: namjar
Date: Fri, 13 Mar 2026 19:51:58 -0700
Subject: [PATCH 008/101] chore(web): install lucide-react and create component
library directory
Co-Authored-By: Claude Opus 4.6
---
pnpm-lock.yaml | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7848e5ee..1f381cd7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -144,6 +144,9 @@ importers:
'@sentry/nextjs':
specifier: ^10.43.0
version: 10.43.0(@opentelemetry/context-async-hooks@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.6.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.6.0(@opentelemetry/api@1.9.0))(next@16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.105.4)
+ lucide-react:
+ specifier: ^0.577.0
+ version: 0.577.0(react@19.2.3)
next:
specifier: 16.1.6
version: 16.1.6(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -3637,6 +3640,11 @@ packages:
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+ lucide-react@0.577.0:
+ resolution: {integrity: sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
@@ -8684,6 +8692,10 @@ snapshots:
dependencies:
yallist: 3.1.1
+ lucide-react@0.577.0(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
From 68c50f2ea4c99ee252787d49ec2ec4ceb8f74032 Mon Sep 17 00:00:00 2001
From: namjar
Date: Sat, 14 Mar 2026 01:47:58 -0700
Subject: [PATCH 009/101] feat: add strategy pattern to CostBasisCalculator +
landing global roadmap
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Tax engine: add CostBasisStrategy interface and registerStrategy() API
for pluggable international tax methods (weighted average, moving
average). Fully backward-compatible — existing FIFO/LIFO/HIFO/Specific
ID unchanged. Custom strategies resolve via registry fallback in the
default switch branch.
Landing: add EU (DAC8/Germany/France) & Japan roadmap banner with i18n.
Co-Authored-By: Claude Opus 4.6
---
packages/tax-engine/src/calculator.ts | 43 ++++++++++++++++++++++++++-
packages/tax-engine/src/index.ts | 8 ++++-
packages/tax-engine/src/types.ts | 22 +++++++++++++-
3 files changed, 70 insertions(+), 3 deletions(-)
diff --git a/packages/tax-engine/src/calculator.ts b/packages/tax-engine/src/calculator.ts
index 6079fbfd..e00e2dce 100644
--- a/packages/tax-engine/src/calculator.ts
+++ b/packages/tax-engine/src/calculator.ts
@@ -1,5 +1,10 @@
/**
* CostBasisCalculator — unified entry point for tax calculations.
+ *
+ * Supports built-in methods (FIFO, LIFO, HIFO, SPECIFIC_ID) and
+ * custom strategies via registerStrategy() for international tax
+ * methods (e.g., weighted average, moving average).
+ *
* @license AGPL-3.0
*/
@@ -12,9 +17,39 @@ import type {
TaxableEvent,
CalculationResult,
CostBasisMethod,
+ CostBasisStrategy,
LotSelection,
} from "./types";
+/** Global registry of custom strategies */
+const strategyRegistry = new Map();
+
+/**
+ * Register a custom cost basis calculation strategy.
+ *
+ * @example
+ * ```typescript
+ * registerStrategy({
+ * name: "WEIGHTED_AVG",
+ * calculate(lots, event, strictSilo) { ... }
+ * });
+ * const calc = new CostBasisCalculator("WEIGHTED_AVG");
+ * ```
+ */
+export function registerStrategy(strategy: CostBasisStrategy): void {
+ strategyRegistry.set(strategy.name, strategy);
+}
+
+/** Get a registered custom strategy by name */
+export function getStrategy(name: string): CostBasisStrategy | undefined {
+ return strategyRegistry.get(name);
+}
+
+/** List all registered custom strategy names */
+export function getRegisteredStrategies(): string[] {
+ return Array.from(strategyRegistry.keys());
+}
+
export class CostBasisCalculator {
private method: CostBasisMethod;
private lots: TaxLot[] = [];
@@ -44,8 +79,14 @@ export class CostBasisCalculator {
throw new Error(
"SPECIFIC_ID requires lot selections — use calculateSpecificId()",
);
- default:
+ default: {
+ // Check custom strategy registry
+ const strategy = strategyRegistry.get(this.method);
+ if (strategy) {
+ return strategy.calculate(this.lots, event, strictSilo);
+ }
throw new Error(`Unknown method: ${this.method}`);
+ }
}
}
diff --git a/packages/tax-engine/src/index.ts b/packages/tax-engine/src/index.ts
index a0db8763..1a98986e 100644
--- a/packages/tax-engine/src/index.ts
+++ b/packages/tax-engine/src/index.ts
@@ -12,12 +12,18 @@ export { calculateFIFO } from "./methods/fifo";
export { calculateLIFO } from "./methods/lifo";
export { calculateHIFO } from "./methods/hifo";
export { calculateSpecificId } from "./methods/specific-id";
-export { CostBasisCalculator } from "./calculator";
+export {
+ CostBasisCalculator,
+ registerStrategy,
+ getStrategy,
+ getRegisteredStrategies,
+} from "./calculator";
export type {
TaxLot,
TaxableEvent,
CalculationResult,
CostBasisMethod,
+ CostBasisStrategy,
LotSelection,
} from "./types";
diff --git a/packages/tax-engine/src/types.ts b/packages/tax-engine/src/types.ts
index 3affc789..d2b6454f 100644
--- a/packages/tax-engine/src/types.ts
+++ b/packages/tax-engine/src/types.ts
@@ -4,7 +4,27 @@
*/
/** Supported cost basis calculation methods */
-export type CostBasisMethod = "FIFO" | "LIFO" | "HIFO" | "SPECIFIC_ID";
+export type CostBasisMethod =
+ | "FIFO"
+ | "LIFO"
+ | "HIFO"
+ | "SPECIFIC_ID"
+ | (string & {});
+
+/**
+ * Strategy interface for pluggable cost basis calculation methods.
+ * Implement this to add new methods (e.g., weighted average, moving average).
+ */
+export interface CostBasisStrategy {
+ /** Unique method name */
+ readonly name: string;
+ /** Calculate gains/losses for a taxable event */
+ calculate(
+ lots: TaxLot[],
+ event: TaxableEvent,
+ strictSilo?: boolean,
+ ): CalculationResult;
+}
/** Lot selection for Specific ID method */
export interface LotSelection {
From a152dbf6a8941d682a1161540483576376b4be70 Mon Sep 17 00:00:00 2001
From: namjar
Date: Sat, 14 Mar 2026 03:38:29 -0700
Subject: [PATCH 010/101] docs: update README with dTax branding, logo, and 6
language translations
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Rename DTax→dTax in README.md, add logo image and language switcher
- Add README.zh.md (简体中文), README.zh-Hant.md (繁體中文)
- Add README.ja.md (日本語), README.ko.md (한국어)
- Add README.es.md (Español), README.pt.md (Português)
Co-Authored-By: Claude Opus 4.6
---
README.es.md | 171 ++++++++++++++++++++++++++++++++++++++++++++++
README.ja.md | 171 ++++++++++++++++++++++++++++++++++++++++++++++
README.ko.md | 171 ++++++++++++++++++++++++++++++++++++++++++++++
README.md | 10 ++-
README.pt.md | 171 ++++++++++++++++++++++++++++++++++++++++++++++
README.zh-Hant.md | 171 ++++++++++++++++++++++++++++++++++++++++++++++
README.zh.md | 171 ++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 1033 insertions(+), 3 deletions(-)
create mode 100644 README.es.md
create mode 100644 README.ja.md
create mode 100644 README.ko.md
create mode 100644 README.pt.md
create mode 100644 README.zh-Hant.md
create mode 100644 README.zh.md
diff --git a/README.es.md b/README.es.md
new file mode 100644
index 00000000..f42b790d
--- /dev/null
+++ b/README.es.md
@@ -0,0 +1,171 @@
+
+
+
dTax
+
+ El único motor completo de impuestos cripto en TypeScript disponible en npm
+
+
+
+
+
+
+
+
+ English · 简体中文 · 繁體中文 · Español · 日本語 · 한국어 · Português
+
+
+
+---
+
+**23 parsers de exchanges** | **FIFO / LIFO / HIFO / Specific ID** | **Form 8949 + Schedule D** | **Detección de wash sales** | **Simulador de escenarios**
+
+## Instalación
+
+```bash
+npm install @dtax/tax-engine
+```
+
+## Ejemplo rápido
+
+```typescript
+import {
+ parseCsv,
+ CostBasisCalculator,
+ generateForm8949,
+} from "@dtax/tax-engine";
+
+// 1. Parse any exchange CSV (auto-detects format)
+const { lots, events } = parseCsv(csvString);
+
+// 2. Calculate gains/losses
+const calc = new CostBasisCalculator("FIFO");
+calc.addLots(lots);
+const results = events.map((e) => calc.calculate(e));
+
+// 3. Generate IRS Form 8949
+const report = generateForm8949(results);
+```
+
+## CLI
+
+Calcula impuestos desde la línea de comandos sin escribir código:
+
+```bash
+npx @dtax/cli calculate trades.csv --method FIFO
+npx @dtax/cli calculate trades.csv --method LIFO --year 2025 --json
+npx @dtax/cli calculate coinbase.csv binance.csv --method HIFO
+```
+
+Instala globalmente para uso frecuente:
+
+```bash
+npm install -g @dtax/cli
+dtax calculate trades.csv --method FIFO --form8949 report.csv
+dtax calculate trades.csv --schedule-d --include-wash-sales
+```
+
+## Exchanges soportados (23 parsers)
+
+Todos los parsers detectan automáticamente el formato CSV. No requiere configuración.
+
+| Categoría | Exchanges |
+| ----------- | ----------------------------------------------------------------------------------- |
+| Principales | Coinbase, Binance, Binance US, Kraken, Gemini |
+| Globales | KuCoin, OKX, Bybit, Gate.io, Bitget, MEXC, HTX (Huobi) |
+| Otros | Crypto.com, Bitfinex, Poloniex |
+| On-chain | Etherscan (ETH + ERC-20 + BSC/Polygon/Avalanche/Fantom), Solscan (SOL + SPL + DeFi) |
+| Migración | Koinly, CoinTracker, Cryptact (importar desde competidores) |
+| Genérico | Generic CSV (mapea tus propias columnas) |
+
+## Características
+
+- **4 métodos de base de costo** -- FIFO, LIFO, HIFO, Specific ID (conforme al IRS)
+- **Form 8949** -- Exportación en CSV, PDF y TXF (TurboTax) con clasificación Box A-F
+- **Schedule D** -- Agregación Part I/II, límite de pérdidas de $3,000, cálculo de arrastre
+- **Detección de wash sales** -- Ventana de 30 días, rechazo parcial, código W en Form 8949
+- **Simulador de escenarios** -- Previsualiza el impacto fiscal antes de vender (`simulateSale()`)
+- **Comparación de métodos** -- Encuentra el método óptimo entre FIFO/LIFO/HIFO (`compareAllMethods()`)
+- **Soporte DeFi + NFT** -- Depósitos/retiros de LP, staking, wraps, bridges, 12 tipos de transacciones DeFi
+- **Reconciliación 1099-DA** -- Coincidencia en 3 fases contra datos reportados por brokers
+- **Análisis de portafolio** -- Agregación de posiciones, P&L no realizado, oportunidades de tax-loss harvesting
+- **Contabilidad aislada por wallet** -- Aislamiento estricto de base de costo por wallet
+- **Detección de transferencias internas** -- Detección automática de transferencias entre tus propias wallets
+
+## Comparación con alternativas
+
+| Característica | dTax | Rotki | RP2 |
+| ------------------------------- | :--------: | :-----: | :------: |
+| Lenguaje | TypeScript | Python | Python |
+| Instalable vía npm | Yes | No | No |
+| Parsers de exchanges | 23 | 15 | 8 |
+| Métodos de base de costo | 4 | 3 | 3 |
+| Form 8949 PDF | Yes | No | No |
+| Exportación TXF TurboTax | Yes | No | No |
+| Generación de Schedule D | Yes | No | No |
+| Detección de wash sales | Yes | Yes | No |
+| Simulador de escenarios | Yes | No | No |
+| Comparación de métodos | Yes | No | No |
+| Tipos de tx DeFi/NFT | 12 | 8 | 4 |
+| Reconciliación 1099-DA | Yes | No | No |
+| Importación CSV de competidores | Yes | No | No |
+| Herramienta CLI | Yes | Yes | Yes |
+| Navegador/Node.js | Both | Desktop | CLI only |
+
+## Paquetes
+
+| Paquete | Descripción |
+| --------------------------------------------- | --------------------------------------------- |
+| [`@dtax/tax-engine`](packages/tax-engine) | Motor de cálculo principal, parsers, reportes |
+| [`@dtax/cli`](packages/cli) | Interfaz de línea de comandos |
+| [`@dtax/shared-types`](packages/shared-types) | Definiciones de tipos TypeScript |
+
+## Aspectos destacados de la API
+
+```typescript
+// Cost basis calculation
+import { calculateFIFO, calculateLIFO, calculateHIFO } from "@dtax/tax-engine";
+
+// Reports
+import {
+ generateForm8949,
+ form8949ToCsv,
+ generateForm8949Pdf,
+} from "@dtax/tax-engine";
+import { generateScheduleD } from "@dtax/tax-engine";
+import { form8949ToTxf } from "@dtax/tax-engine"; // TurboTax
+
+// Analysis
+import { detectWashSales } from "@dtax/tax-engine";
+import { simulateSale } from "@dtax/tax-engine"; // what-if
+import { compareAllMethods } from "@dtax/tax-engine"; // optimizer
+import { analyzeHoldings } from "@dtax/tax-engine"; // portfolio
+
+// Parsers (auto-detect or use individually)
+import { parseCsv, detectCsvFormat } from "@dtax/tax-engine";
+```
+
+## Contribuir
+
+Las contribuciones son bienvenidas. Consulta [CONTRIBUTING.md](CONTRIBUTING.md) para las directrices.
+
+```bash
+# Prerequisites: Node.js >= 20, pnpm >= 9
+git clone https://github.com/dTaxLab/dtax.git && cd dtax
+pnpm install
+pnpm test # 800+ tests across all packages
+pnpm build # build all packages
+```
+
+## Licencia
+
+Todos los paquetes en este repositorio están licenciados bajo [AGPL-3.0](LICENSE).
+
+Esto significa que puedes usar dTax libremente en tus proyectos. Si modificas el código fuente y lo distribuyes (incluyendo como un servicio en red), debes publicar tus modificaciones bajo AGPL-3.0.
+
+Para consultas sobre licencias comerciales: [getdtax.com](https://getdtax.com)
+
+## Enlaces
+
+- Sitio web: [getdtax.com](https://getdtax.com)
+- Problemas: [GitHub Issues](https://github.com/dTaxLab/dtax/issues)
+- Discusiones: [GitHub Discussions](https://github.com/dTaxLab/dtax/discussions)
diff --git a/README.ja.md b/README.ja.md
new file mode 100644
index 00000000..f77d14a2
--- /dev/null
+++ b/README.ja.md
@@ -0,0 +1,171 @@
+
+
+
dTax
+
+ npm で利用できる唯一の完全な TypeScript 暗号資産税務エンジン
+
+
+
+
+
+
+
+
+ English · 简体中文 · 繁體中文 · Español · 日本語 · 한국어 · Português
+
+
+
+---
+
+**23 種の取引所パーサー** | **FIFO / LIFO / HIFO / Specific ID** | **Form 8949 + Schedule D** | **ウォッシュセール検出** | **What-if シミュレーター**
+
+## インストール
+
+```bash
+npm install @dtax/tax-engine
+```
+
+## クイック例
+
+```typescript
+import {
+ parseCsv,
+ CostBasisCalculator,
+ generateForm8949,
+} from "@dtax/tax-engine";
+
+// 1. 任意の取引所 CSV を解析(フォーマット自動検出)
+const { lots, events } = parseCsv(csvString);
+
+// 2. 損益を計算
+const calc = new CostBasisCalculator("FIFO");
+calc.addLots(lots);
+const results = events.map((e) => calc.calculate(e));
+
+// 3. IRS Form 8949 を生成
+const report = generateForm8949(results);
+```
+
+## CLI
+
+コードを書かずにコマンドラインから税金を計算できます:
+
+```bash
+npx @dtax/cli calculate trades.csv --method FIFO
+npx @dtax/cli calculate trades.csv --method LIFO --year 2025 --json
+npx @dtax/cli calculate coinbase.csv binance.csv --method HIFO
+```
+
+グローバルインストールして繰り返し使用:
+
+```bash
+npm install -g @dtax/cli
+dtax calculate trades.csv --method FIFO --form8949 report.csv
+dtax calculate trades.csv --schedule-d --include-wash-sales
+```
+
+## 対応取引所(23 パーサー)
+
+すべてのパーサーは CSV フォーマットを自動検出します。設定は不要です。
+
+| カテゴリ | 取引所 |
+| -------------- | ----------------------------------------------------------------------------------- |
+| 主要 | Coinbase, Binance, Binance US, Kraken, Gemini |
+| グローバル | KuCoin, OKX, Bybit, Gate.io, Bitget, MEXC, HTX (Huobi) |
+| その他 | Crypto.com, Bitfinex, Poloniex |
+| オンチェーン | Etherscan (ETH + ERC-20 + BSC/Polygon/Avalanche/Fantom), Solscan (SOL + SPL + DeFi) |
+| 移行 | Koinly, CoinTracker, Cryptact(競合サービスからのインポート) |
+| フォールバック | Generic CSV(独自のカラムをマッピング) |
+
+## 機能
+
+- **4 種類の取得原価計算方式** -- FIFO、LIFO、HIFO、Specific ID(IRS 準拠)
+- **Form 8949** -- CSV、PDF、TXF(TurboTax)エクスポート、Box A-F 分類対応
+- **Schedule D** -- Part I/II 集計、$3,000 損失上限、繰越計算
+- **ウォッシュセール検出** -- 30 日間ウィンドウ、部分的否認、Form 8949 コード W
+- **What-if シミュレーター** -- 売却前に税金への影響をプレビュー(`simulateSale()`)
+- **方式比較** -- FIFO/LIFO/HIFO 全方式で最適な方式を検索(`compareAllMethods()`)
+- **DeFi + NFT 対応** -- LP 入出金、ステーキング、ラップ、ブリッジ、12 種類の DeFi トランザクションタイプ
+- **1099-DA 照合** -- ブローカー報告データとの 3 フェーズマッチング
+- **ポートフォリオ分析** -- 保有資産集計、未実現損益、タックスロスハーベスティング機会
+- **ウォレット分離会計** -- ウォレットごとの厳密な取得原価分離
+- **内部送金マッチング** -- 自分のウォレット間の送金を自動検出
+
+## 代替ツールとの比較
+
+| 機能 | dTax | Rotki | RP2 |
+| ------------------------------- | :--------: | :-----: | :------: |
+| 言語 | TypeScript | Python | Python |
+| npm インストール可能 | Yes | No | No |
+| 取引所パーサー | 23 | 15 | 8 |
+| 取得原価計算方式 | 4 | 3 | 3 |
+| Form 8949 PDF | Yes | No | No |
+| TurboTax TXF エクスポート | Yes | No | No |
+| Schedule D 生成 | Yes | No | No |
+| ウォッシュセール検出 | Yes | Yes | No |
+| What-if シミュレーター | Yes | No | No |
+| 方式比較 | Yes | No | No |
+| DeFi/NFT トランザクションタイプ | 12 | 8 | 4 |
+| 1099-DA 照合 | Yes | No | No |
+| 競合 CSV インポート | Yes | No | No |
+| CLI ツール | Yes | Yes | Yes |
+| ブラウザ/Node.js | Both | Desktop | CLI only |
+
+## パッケージ
+
+| パッケージ | 説明 |
+| --------------------------------------------- | ------------------------------------ |
+| [`@dtax/tax-engine`](packages/tax-engine) | コア計算エンジン、パーサー、レポート |
+| [`@dtax/cli`](packages/cli) | コマンドラインインターフェース |
+| [`@dtax/shared-types`](packages/shared-types) | TypeScript 型定義 |
+
+## API ハイライト
+
+```typescript
+// 取得原価計算
+import { calculateFIFO, calculateLIFO, calculateHIFO } from "@dtax/tax-engine";
+
+// レポート
+import {
+ generateForm8949,
+ form8949ToCsv,
+ generateForm8949Pdf,
+} from "@dtax/tax-engine";
+import { generateScheduleD } from "@dtax/tax-engine";
+import { form8949ToTxf } from "@dtax/tax-engine"; // TurboTax
+
+// 分析
+import { detectWashSales } from "@dtax/tax-engine";
+import { simulateSale } from "@dtax/tax-engine"; // what-if
+import { compareAllMethods } from "@dtax/tax-engine"; // オプティマイザー
+import { analyzeHoldings } from "@dtax/tax-engine"; // ポートフォリオ
+
+// パーサー(自動検出または個別使用)
+import { parseCsv, detectCsvFormat } from "@dtax/tax-engine";
+```
+
+## コントリビューション
+
+コントリビューションを歓迎します。ガイドラインは [CONTRIBUTING.md](CONTRIBUTING.md) をご覧ください。
+
+```bash
+# 前提条件: Node.js >= 20, pnpm >= 9
+git clone https://github.com/dTaxLab/dtax.git && cd dtax
+pnpm install
+pnpm test # 全パッケージで 800 以上のテスト
+pnpm build # 全パッケージをビルド
+```
+
+## ライセンス
+
+このリポジトリのすべてのパッケージは [AGPL-3.0](LICENSE) の下でライセンスされています。
+
+dTax はプロジェクトで自由にご利用いただけます。ソースコードを改変して配布する場合(ネットワークサービスとしての提供を含む)、改変部分を AGPL-3.0 の下で公開する必要があります。
+
+商用ライセンスに関するお問い合わせ: [getdtax.com](https://getdtax.com)
+
+## リンク
+
+- ウェブサイト: [getdtax.com](https://getdtax.com)
+- Issues: [GitHub Issues](https://github.com/dTaxLab/dtax/issues)
+- ディスカッション: [GitHub Discussions](https://github.com/dTaxLab/dtax/discussions)
diff --git a/README.ko.md b/README.ko.md
new file mode 100644
index 00000000..015036fa
--- /dev/null
+++ b/README.ko.md
@@ -0,0 +1,171 @@
+
+
+
dTax
+
+ npm에서 유일한 완전한 TypeScript 암호화폐 세금 엔진
+
+
+
+
+
+
+
+
+ English · 简体中文 · 繁體中文 · Español · 日本語 · 한국어 · Português
+
+
+
+---
+
+**23개 거래소 파서** | **FIFO / LIFO / HIFO / Specific ID** | **Form 8949 + Schedule D** | **워시세일 감지** | **가상 매도 시뮬레이터**
+
+## 설치
+
+```bash
+npm install @dtax/tax-engine
+```
+
+## 빠른 예제
+
+```typescript
+import {
+ parseCsv,
+ CostBasisCalculator,
+ generateForm8949,
+} from "@dtax/tax-engine";
+
+// 1. 거래소 CSV 파싱 (형식 자동 감지)
+const { lots, events } = parseCsv(csvString);
+
+// 2. 자본이득/손실 계산
+const calc = new CostBasisCalculator("FIFO");
+calc.addLots(lots);
+const results = events.map((e) => calc.calculate(e));
+
+// 3. IRS Form 8949 생성
+const report = generateForm8949(results);
+```
+
+## CLI
+
+코드 작성 없이 커맨드 라인에서 세금을 계산하세요:
+
+```bash
+npx @dtax/cli calculate trades.csv --method FIFO
+npx @dtax/cli calculate trades.csv --method LIFO --year 2025 --json
+npx @dtax/cli calculate coinbase.csv binance.csv --method HIFO
+```
+
+반복 사용을 위해 전역 설치:
+
+```bash
+npm install -g @dtax/cli
+dtax calculate trades.csv --method FIFO --form8949 report.csv
+dtax calculate trades.csv --schedule-d --include-wash-sales
+```
+
+## 지원 거래소 (23개 파서)
+
+모든 파서가 CSV 형식을 자동 감지합니다. 별도의 설정이 필요 없습니다.
+
+| 분류 | 거래소 |
+| ------------ | ----------------------------------------------------------------------------------- |
+| 주요 | Coinbase, Binance, Binance US, Kraken, Gemini |
+| 글로벌 | KuCoin, OKX, Bybit, Gate.io, Bitget, MEXC, HTX (Huobi) |
+| 기타 | Crypto.com, Bitfinex, Poloniex |
+| 온체인 | Etherscan (ETH + ERC-20 + BSC/Polygon/Avalanche/Fantom), Solscan (SOL + SPL + DeFi) |
+| 마이그레이션 | Koinly, CoinTracker, Cryptact (경쟁사에서 가져오기) |
+| 대체 | Generic CSV (직접 열 매핑) |
+
+## 주요 기능
+
+- **4가지 원가 기준 방식** -- FIFO, LIFO, HIFO, Specific ID (IRS 규정 준수)
+- **Form 8949** -- CSV, PDF, TXF (TurboTax) 내보내기 및 Box A-F 분류
+- **Schedule D** -- Part I/II 합산, $3,000 손실 한도, 이월 계산
+- **워시세일 감지** -- 30일 기간, 부분 비허용, Form 8949 코드 W
+- **가상 매도 시뮬레이터** -- 매도 전 세금 영향 미리보기 (`simulateSale()`)
+- **방식 비교** -- FIFO/LIFO/HIFO 중 최적 방식 찾기 (`compareAllMethods()`)
+- **DeFi + NFT 지원** -- LP 예치/출금, 스테이킹, 랩핑, 브릿지, 12개 DeFi 거래 유형
+- **1099-DA 대사** -- 브로커 보고 데이터와 3단계 매칭
+- **포트폴리오 분석** -- 보유 자산 합산, 미실현 손익, 세금 손실 수확 기회
+- **지갑별 분리 회계** -- 지갑별 엄격한 원가 기준 분리
+- **내부 이체 매칭** -- 본인 소유 지갑 간 이체 자동 감지
+
+## 대안과의 비교
+
+| 기능 | dTax | Rotki | RP2 |
+| --------------------- | :--------: | :-----: | :------: |
+| 언어 | TypeScript | Python | Python |
+| npm 설치 가능 | Yes | No | No |
+| 거래소 파서 | 23 | 15 | 8 |
+| 원가 기준 방식 | 4 | 3 | 3 |
+| Form 8949 PDF | Yes | No | No |
+| TurboTax TXF 내보내기 | Yes | No | No |
+| Schedule D 생성 | Yes | No | No |
+| 워시세일 감지 | Yes | Yes | No |
+| 가상 매도 시뮬레이터 | Yes | No | No |
+| 방식 비교 | Yes | No | No |
+| DeFi/NFT 거래 유형 | 12 | 8 | 4 |
+| 1099-DA 대사 | Yes | No | No |
+| 경쟁사 CSV 가져오기 | Yes | No | No |
+| CLI 도구 | Yes | Yes | Yes |
+| 브라우저/Node.js | Both | Desktop | CLI only |
+
+## 패키지
+
+| 패키지 | 설명 |
+| --------------------------------------------- | ---------------------------- |
+| [`@dtax/tax-engine`](packages/tax-engine) | 핵심 계산 엔진, 파서, 보고서 |
+| [`@dtax/cli`](packages/cli) | 커맨드 라인 인터페이스 |
+| [`@dtax/shared-types`](packages/shared-types) | TypeScript 타입 정의 |
+
+## API 하이라이트
+
+```typescript
+// 원가 기준 계산
+import { calculateFIFO, calculateLIFO, calculateHIFO } from "@dtax/tax-engine";
+
+// 보고서
+import {
+ generateForm8949,
+ form8949ToCsv,
+ generateForm8949Pdf,
+} from "@dtax/tax-engine";
+import { generateScheduleD } from "@dtax/tax-engine";
+import { form8949ToTxf } from "@dtax/tax-engine"; // TurboTax
+
+// 분석
+import { detectWashSales } from "@dtax/tax-engine";
+import { simulateSale } from "@dtax/tax-engine"; // 가상 매도
+import { compareAllMethods } from "@dtax/tax-engine"; // 최적화
+import { analyzeHoldings } from "@dtax/tax-engine"; // 포트폴리오
+
+// 파서 (자동 감지 또는 개별 사용)
+import { parseCsv, detectCsvFormat } from "@dtax/tax-engine";
+```
+
+## 기여하기
+
+기여를 환영합니다. 가이드라인은 [CONTRIBUTING.md](CONTRIBUTING.md)를 참고하세요.
+
+```bash
+# 필수 조건: Node.js >= 20, pnpm >= 9
+git clone https://github.com/dTaxLab/dtax.git && cd dtax
+pnpm install
+pnpm test # 모든 패키지에 걸쳐 800개 이상의 테스트
+pnpm build # 모든 패키지 빌드
+```
+
+## 라이선스
+
+이 저장소의 모든 패키지는 [AGPL-3.0](LICENSE) 라이선스를 따릅니다.
+
+dTax를 프로젝트에서 자유롭게 사용할 수 있습니다. 소스를 수정하여 배포하는 경우 (네트워크 서비스 포함), 수정 사항을 AGPL-3.0으로 공개해야 합니다.
+
+상업용 라이선스 문의: [getdtax.com](https://getdtax.com)
+
+## 링크
+
+- 웹사이트: [getdtax.com](https://getdtax.com)
+- 이슈: [GitHub Issues](https://github.com/dTaxLab/dtax/issues)
+- 토론: [GitHub Discussions](https://github.com/dTaxLab/dtax/discussions)
diff --git a/README.md b/README.md
index 066ac43c..5529f28e 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
-
DTax
+
+ dTax
The only complete TypeScript crypto tax engine on npm
@@ -9,6 +10,9 @@
+
+ English · 简体中文 · 繁體中文 · Español · 日本語 · 한국어 · Português
+
---
@@ -89,7 +93,7 @@ All parsers auto-detect the CSV format. No configuration required.
## Comparison with Alternatives
-| Feature | DTax | Rotki | RP2 |
+| Feature | dTax | Rotki | RP2 |
| ---------------------- | :--------: | :-----: | :------: |
| Language | TypeScript | Python | Python |
| npm installable | Yes | No | No |
@@ -156,7 +160,7 @@ pnpm build # build all packages
All packages in this repository are licensed under [AGPL-3.0](LICENSE).
-This means you can use DTax freely in your projects. If you modify the source and distribute it (including as a network service), you must release your modifications under AGPL-3.0.
+This means you can use dTax freely in your projects. If you modify the source and distribute it (including as a network service), you must release your modifications under AGPL-3.0.
For commercial licensing inquiries: [getdtax.com](https://getdtax.com)
diff --git a/README.pt.md b/README.pt.md
new file mode 100644
index 00000000..4a55b5e4
--- /dev/null
+++ b/README.pt.md
@@ -0,0 +1,171 @@
+
+
+
dTax
+
+ O único motor completo de impostos sobre criptomoedas em TypeScript no npm
+
+
+
+
+
+
+
+
+ English · 简体中文 · 繁體中文 · Español · 日本語 · 한국어 · Português
+
+
+
+---
+
+**23 parsers de exchanges** | **FIFO / LIFO / HIFO / Specific ID** | **Form 8949 + Schedule D** | **Detecção de wash sale** | **Simulador what-if**
+
+## Instalação
+
+```bash
+npm install @dtax/tax-engine
+```
+
+## Exemplo Rápido
+
+```typescript
+import {
+ parseCsv,
+ CostBasisCalculator,
+ generateForm8949,
+} from "@dtax/tax-engine";
+
+// 1. Parse any exchange CSV (auto-detects format)
+const { lots, events } = parseCsv(csvString);
+
+// 2. Calculate gains/losses
+const calc = new CostBasisCalculator("FIFO");
+calc.addLots(lots);
+const results = events.map((e) => calc.calculate(e));
+
+// 3. Generate IRS Form 8949
+const report = generateForm8949(results);
+```
+
+## CLI
+
+Calcule impostos pela linha de comando sem escrever nenhum código:
+
+```bash
+npx @dtax/cli calculate trades.csv --method FIFO
+npx @dtax/cli calculate trades.csv --method LIFO --year 2025 --json
+npx @dtax/cli calculate coinbase.csv binance.csv --method HIFO
+```
+
+Instale globalmente para uso frequente:
+
+```bash
+npm install -g @dtax/cli
+dtax calculate trades.csv --method FIFO --form8949 report.csv
+dtax calculate trades.csv --schedule-d --include-wash-sales
+```
+
+## Exchanges Suportadas (23 Parsers)
+
+Todos os parsers detectam automaticamente o formato do CSV. Nenhuma configuração necessária.
+
+| Categoria | Exchanges |
+| ---------- | ----------------------------------------------------------------------------------- |
+| Principais | Coinbase, Binance, Binance US, Kraken, Gemini |
+| Globais | KuCoin, OKX, Bybit, Gate.io, Bitget, MEXC, HTX (Huobi) |
+| Outras | Crypto.com, Bitfinex, Poloniex |
+| On-chain | Etherscan (ETH + ERC-20 + BSC/Polygon/Avalanche/Fantom), Solscan (SOL + SPL + DeFi) |
+| Migração | Koinly, CoinTracker, Cryptact (importação de concorrentes) |
+| Genérico | Generic CSV (mapeie suas próprias colunas) |
+
+## Funcionalidades
+
+- **4 métodos de custo base** -- FIFO, LIFO, HIFO, Specific ID (em conformidade com o IRS)
+- **Form 8949** -- Exportação em CSV, PDF e TXF (TurboTax) com classificação Box A-F
+- **Schedule D** -- Agregação Parte I/II, limite de perda de $3.000, cálculo de compensação
+- **Detecção de wash sale** -- Janela de 30 dias, desqualificação parcial, código W no Form 8949
+- **Simulador what-if** -- Visualize o impacto fiscal antes de vender (`simulateSale()`)
+- **Comparação de métodos** -- Encontre o método ideal entre FIFO/LIFO/HIFO (`compareAllMethods()`)
+- **Suporte a DeFi + NFT** -- Depósitos/retiradas de LP, staking, wraps, bridges, 12 tipos de transação DeFi
+- **Reconciliação 1099-DA** -- Correspondência em 3 fases com dados reportados por corretoras
+- **Análise de portfólio** -- Agregação de posições, P&L não realizado, oportunidades de tax-loss harvesting
+- **Contabilidade isolada por carteira** -- Isolamento estrito do custo base por carteira
+- **Correspondência de transferências internas** -- Detecção automática de transferências entre suas próprias carteiras
+
+## Comparação com Alternativas
+
+| Funcionalidade | dTax | Rotki | RP2 |
+| --------------------------- | :--------: | :-----: | :--------: |
+| Linguagem | TypeScript | Python | Python |
+| Instalável via npm | Sim | Não | Não |
+| Parsers de exchanges | 23 | 15 | 8 |
+| Métodos de custo base | 4 | 3 | 3 |
+| Form 8949 PDF | Sim | Não | Não |
+| Exportação TXF TurboTax | Sim | Não | Não |
+| Geração de Schedule D | Sim | Não | Não |
+| Detecção de wash sale | Sim | Sim | Não |
+| Simulador what-if | Sim | Não | Não |
+| Comparação de métodos | Sim | Não | Não |
+| Tipos de tx DeFi/NFT | 12 | 8 | 4 |
+| Reconciliação 1099-DA | Sim | Não | Não |
+| Importação CSV concorrentes | Sim | Não | Não |
+| Ferramenta CLI | Sim | Sim | Sim |
+| Browser/Node.js | Ambos | Desktop | Apenas CLI |
+
+## Pacotes
+
+| Pacote | Descrição |
+| --------------------------------------------- | ----------------------------------------------- |
+| [`@dtax/tax-engine`](packages/tax-engine) | Motor de cálculo principal, parsers, relatórios |
+| [`@dtax/cli`](packages/cli) | Interface de linha de comando |
+| [`@dtax/shared-types`](packages/shared-types) | Definições de tipos TypeScript |
+
+## Destaques da API
+
+```typescript
+// Cost basis calculation
+import { calculateFIFO, calculateLIFO, calculateHIFO } from "@dtax/tax-engine";
+
+// Reports
+import {
+ generateForm8949,
+ form8949ToCsv,
+ generateForm8949Pdf,
+} from "@dtax/tax-engine";
+import { generateScheduleD } from "@dtax/tax-engine";
+import { form8949ToTxf } from "@dtax/tax-engine"; // TurboTax
+
+// Analysis
+import { detectWashSales } from "@dtax/tax-engine";
+import { simulateSale } from "@dtax/tax-engine"; // what-if
+import { compareAllMethods } from "@dtax/tax-engine"; // optimizer
+import { analyzeHoldings } from "@dtax/tax-engine"; // portfolio
+
+// Parsers (auto-detect or use individually)
+import { parseCsv, detectCsvFormat } from "@dtax/tax-engine";
+```
+
+## Contribuindo
+
+Contribuições são bem-vindas. Consulte [CONTRIBUTING.md](CONTRIBUTING.md) para as diretrizes.
+
+```bash
+# Prerequisites: Node.js >= 20, pnpm >= 9
+git clone https://github.com/dTaxLab/dtax.git && cd dtax
+pnpm install
+pnpm test # 800+ tests across all packages
+pnpm build # build all packages
+```
+
+## Licença
+
+Todos os pacotes neste repositório são licenciados sob [AGPL-3.0](LICENSE).
+
+Isso significa que você pode usar o dTax livremente em seus projetos. Se você modificar o código-fonte e distribuí-lo (incluindo como um serviço de rede), você deve disponibilizar suas modificações sob AGPL-3.0.
+
+Para consultas sobre licenciamento comercial: [getdtax.com](https://getdtax.com)
+
+## Links
+
+- Website: [getdtax.com](https://getdtax.com)
+- Issues: [GitHub Issues](https://github.com/dTaxLab/dtax/issues)
+- Discussões: [GitHub Discussions](https://github.com/dTaxLab/dtax/discussions)
diff --git a/README.zh-Hant.md b/README.zh-Hant.md
new file mode 100644
index 00000000..f3a2dae4
--- /dev/null
+++ b/README.zh-Hant.md
@@ -0,0 +1,171 @@
+
+
+
dTax
+
+ npm 上唯一完整的 TypeScript 加密貨幣稅務引擎
+
+
+
+
+
+
+
+
+ English · 简体中文 · 繁體中文 · Español · 日本語 · 한국어 · Português
+
+
+
+---
+
+**23 個交易所解析器** | **FIFO / LIFO / HIFO / Specific ID** | **Form 8949 + Schedule D** | **洗售偵測** | **模擬試算**
+
+## 安裝
+
+```bash
+npm install @dtax/tax-engine
+```
+
+## 快速範例
+
+```typescript
+import {
+ parseCsv,
+ CostBasisCalculator,
+ generateForm8949,
+} from "@dtax/tax-engine";
+
+// 1. 解析任何交易所的 CSV(自動偵測格式)
+const { lots, events } = parseCsv(csvString);
+
+// 2. 計算損益
+const calc = new CostBasisCalculator("FIFO");
+calc.addLots(lots);
+const results = events.map((e) => calc.calculate(e));
+
+// 3. 產生 IRS Form 8949
+const report = generateForm8949(results);
+```
+
+## CLI
+
+無需撰寫任何程式碼,直接從命令列計算稅務:
+
+```bash
+npx @dtax/cli calculate trades.csv --method FIFO
+npx @dtax/cli calculate trades.csv --method LIFO --year 2025 --json
+npx @dtax/cli calculate coinbase.csv binance.csv --method HIFO
+```
+
+全域安裝以便重複使用:
+
+```bash
+npm install -g @dtax/cli
+dtax calculate trades.csv --method FIFO --form8949 report.csv
+dtax calculate trades.csv --schedule-d --include-wash-sales
+```
+
+## 支援的交易所(23 個解析器)
+
+所有解析器皆自動偵測 CSV 格式,無需任何設定。
+
+| 類別 | 交易所 |
+| -------- | ----------------------------------------------------------------------------------- |
+| 主流 | Coinbase, Binance, Binance US, Kraken, Gemini |
+| 全球 | KuCoin, OKX, Bybit, Gate.io, Bitget, MEXC, HTX (Huobi) |
+| 其他 | Crypto.com, Bitfinex, Poloniex |
+| 鏈上 | Etherscan (ETH + ERC-20 + BSC/Polygon/Avalanche/Fantom), Solscan (SOL + SPL + DeFi) |
+| 遷移匯入 | Koinly, CoinTracker, Cryptact(從競品匯入) |
+| 備用 | Generic CSV(自訂欄位對應) |
+
+## 功能特色
+
+- **4 種成本基礎方法** -- FIFO、LIFO、HIFO、Specific ID(符合 IRS 規範)
+- **Form 8949** -- CSV、PDF 及 TXF(TurboTax)匯出,支援 Box A-F 分類
+- **Schedule D** -- Part I/II 彙總、$3,000 虧損上限、結轉計算
+- **洗售偵測** -- 30 天窗口期、部分不允許扣除、Form 8949 code W
+- **模擬試算** -- 出售前預覽稅務影響(`simulateSale()`)
+- **方法比較** -- 在 FIFO/LIFO/HIFO 中找出最佳方法(`compareAllMethods()`)
+- **DeFi + NFT 支援** -- LP 存取、質押、封裝、跨鏈橋接,12 種 DeFi 交易類型
+- **1099-DA 對帳** -- 3 階段與券商申報資料比對
+- **投資組合分析** -- 持倉彙總、未實現損益、稅損收割機會
+- **錢包隔離記帳** -- 嚴格的逐錢包成本基礎隔離
+- **內部轉帳比對** -- 自動偵測您自有錢包之間的轉帳
+
+## 與替代方案的比較
+
+| 功能 | dTax | Rotki | RP2 |
+| ----------------- | :--------: | :------: | :----: |
+| 語言 | TypeScript | Python | Python |
+| 可透過 npm 安裝 | 是 | 否 | 否 |
+| 交易所解析器 | 23 | 15 | 8 |
+| 成本基礎方法 | 4 | 3 | 3 |
+| Form 8949 PDF | 是 | 否 | 否 |
+| TurboTax TXF 匯出 | 是 | 否 | 否 |
+| Schedule D 產生 | 是 | 否 | 否 |
+| 洗售偵測 | 是 | 是 | 否 |
+| 模擬試算 | 是 | 否 | 否 |
+| 方法比較 | 是 | 否 | 否 |
+| DeFi/NFT 交易類型 | 12 | 8 | 4 |
+| 1099-DA 對帳 | 是 | 否 | 否 |
+| 競品 CSV 匯入 | 是 | 否 | 否 |
+| CLI 工具 | 是 | 是 | 是 |
+| 瀏覽器/Node.js | 兩者皆可 | 桌面應用 | 僅 CLI |
+
+## 套件
+
+| 套件 | 說明 |
+| --------------------------------------------- | -------------------------- |
+| [`@dtax/tax-engine`](packages/tax-engine) | 核心計算引擎、解析器、報表 |
+| [`@dtax/cli`](packages/cli) | 命令列介面 |
+| [`@dtax/shared-types`](packages/shared-types) | TypeScript 型別定義 |
+
+## API 重點功能
+
+```typescript
+// 成本基礎計算
+import { calculateFIFO, calculateLIFO, calculateHIFO } from "@dtax/tax-engine";
+
+// 報表
+import {
+ generateForm8949,
+ form8949ToCsv,
+ generateForm8949Pdf,
+} from "@dtax/tax-engine";
+import { generateScheduleD } from "@dtax/tax-engine";
+import { form8949ToTxf } from "@dtax/tax-engine"; // TurboTax
+
+// 分析
+import { detectWashSales } from "@dtax/tax-engine";
+import { simulateSale } from "@dtax/tax-engine"; // 模擬試算
+import { compareAllMethods } from "@dtax/tax-engine"; // 最佳化
+import { analyzeHoldings } from "@dtax/tax-engine"; // 投資組合
+
+// 解析器(自動偵測或個別使用)
+import { parseCsv, detectCsvFormat } from "@dtax/tax-engine";
+```
+
+## 貢獻
+
+歡迎貢獻。請參閱 [CONTRIBUTING.md](CONTRIBUTING.md) 了解指引。
+
+```bash
+# 前置需求:Node.js >= 20, pnpm >= 9
+git clone https://github.com/dTaxLab/dtax.git && cd dtax
+pnpm install
+pnpm test # 所有套件共 800+ 項測試
+pnpm build # 建置所有套件
+```
+
+## 授權條款
+
+本儲存庫中的所有套件皆以 [AGPL-3.0](LICENSE) 授權。
+
+這代表您可以自由地在專案中使用 dTax。若您修改原始碼並加以散布(包含作為網路服務提供),您必須以 AGPL-3.0 釋出您的修改內容。
+
+商業授權洽詢:[getdtax.com](https://getdtax.com)
+
+## 連結
+
+- 官方網站:[getdtax.com](https://getdtax.com)
+- 問題回報:[GitHub Issues](https://github.com/dTaxLab/dtax/issues)
+- 討論區:[GitHub Discussions](https://github.com/dTaxLab/dtax/discussions)
diff --git a/README.zh.md b/README.zh.md
new file mode 100644
index 00000000..0982fbd6
--- /dev/null
+++ b/README.zh.md
@@ -0,0 +1,171 @@
+
+
+
dTax
+
+ npm 上唯一完整的 TypeScript 加密货币税务引擎
+
+
+
+
+
+
+
+
+ English · 简体中文 · 繁體中文 · Español · 日本語 · 한국어 · Português
+
+
+
+---
+
+**23 个交易所解析器** | **FIFO / LIFO / HIFO / Specific ID** | **Form 8949 + Schedule D** | **洗售检测** | **假设模拟器**
+
+## 安装
+
+```bash
+npm install @dtax/tax-engine
+```
+
+## 快速示例
+
+```typescript
+import {
+ parseCsv,
+ CostBasisCalculator,
+ generateForm8949,
+} from "@dtax/tax-engine";
+
+// 1. Parse any exchange CSV (auto-detects format)
+const { lots, events } = parseCsv(csvString);
+
+// 2. Calculate gains/losses
+const calc = new CostBasisCalculator("FIFO");
+calc.addLots(lots);
+const results = events.map((e) => calc.calculate(e));
+
+// 3. Generate IRS Form 8949
+const report = generateForm8949(results);
+```
+
+## CLI
+
+无需编写任何代码,直接在命令行计算税务:
+
+```bash
+npx @dtax/cli calculate trades.csv --method FIFO
+npx @dtax/cli calculate trades.csv --method LIFO --year 2025 --json
+npx @dtax/cli calculate coinbase.csv binance.csv --method HIFO
+```
+
+全局安装以便反复使用:
+
+```bash
+npm install -g @dtax/cli
+dtax calculate trades.csv --method FIFO --form8949 report.csv
+dtax calculate trades.csv --schedule-d --include-wash-sales
+```
+
+## 支持的交易所(23 个解析器)
+
+所有解析器自动检测 CSV 格式,无需任何配置。
+
+| 类别 | 交易所 |
+| -------- | ----------------------------------------------------------------------------------- |
+| 主流 | Coinbase, Binance, Binance US, Kraken, Gemini |
+| 全球 | KuCoin, OKX, Bybit, Gate.io, Bitget, MEXC, HTX (Huobi) |
+| 其他 | Crypto.com, Bitfinex, Poloniex |
+| 链上 | Etherscan (ETH + ERC-20 + BSC/Polygon/Avalanche/Fantom), Solscan (SOL + SPL + DeFi) |
+| 迁移导入 | Koinly, CoinTracker, Cryptact(从竞品导入) |
+| 通用 | Generic CSV(自定义列映射) |
+
+## 功能特性
+
+- **4 种成本基础方法** -- FIFO、LIFO、HIFO、Specific ID(符合 IRS 规定)
+- **Form 8949** -- CSV、PDF 和 TXF(TurboTax)导出,支持 Box A-F 分类
+- **Schedule D** -- Part I/II 汇总、$3,000 亏损限额、结转计算
+- **洗售检测** -- 30 天窗口期、部分不允许扣除、Form 8949 code W
+- **假设模拟器** -- 卖出前预览税务影响(`simulateSale()`)
+- **方法对比** -- 在 FIFO/LIFO/HIFO 中找到最优方法(`compareAllMethods()`)
+- **DeFi + NFT 支持** -- LP 存取、质押、封装、跨链桥、12 种 DeFi 交易类型
+- **1099-DA 对账** -- 3 阶段与券商报告数据匹配
+- **投资组合分析** -- 持仓汇总、未实现盈亏、税务亏损收割机会
+- **钱包隔离核算** -- 严格按钱包隔离成本基础
+- **内部转账匹配** -- 自动检测你自己钱包之间的转账
+
+## 与替代方案的对比
+
+| 功能特性 | dTax | Rotki | RP2 |
+| ----------------- | :--------: | :-----: | :------: |
+| 编程语言 | TypeScript | Python | Python |
+| 支持 npm 安装 | Yes | No | No |
+| 交易所解析器 | 23 | 15 | 8 |
+| 成本基础方法 | 4 | 3 | 3 |
+| Form 8949 PDF | Yes | No | No |
+| TurboTax TXF 导出 | Yes | No | No |
+| Schedule D 生成 | Yes | No | No |
+| 洗售检测 | Yes | Yes | No |
+| 假设模拟器 | Yes | No | No |
+| 方法对比 | Yes | No | No |
+| DeFi/NFT 交易类型 | 12 | 8 | 4 |
+| 1099-DA 对账 | Yes | No | No |
+| 竞品 CSV 导入 | Yes | No | No |
+| CLI 工具 | Yes | Yes | Yes |
+| 浏览器/Node.js | Both | Desktop | CLI only |
+
+## 包
+
+| 包 | 描述 |
+| --------------------------------------------- | -------------------------- |
+| [`@dtax/tax-engine`](packages/tax-engine) | 核心计算引擎、解析器、报表 |
+| [`@dtax/cli`](packages/cli) | 命令行界面 |
+| [`@dtax/shared-types`](packages/shared-types) | TypeScript 类型定义 |
+
+## API 亮点
+
+```typescript
+// Cost basis calculation
+import { calculateFIFO, calculateLIFO, calculateHIFO } from "@dtax/tax-engine";
+
+// Reports
+import {
+ generateForm8949,
+ form8949ToCsv,
+ generateForm8949Pdf,
+} from "@dtax/tax-engine";
+import { generateScheduleD } from "@dtax/tax-engine";
+import { form8949ToTxf } from "@dtax/tax-engine"; // TurboTax
+
+// Analysis
+import { detectWashSales } from "@dtax/tax-engine";
+import { simulateSale } from "@dtax/tax-engine"; // what-if
+import { compareAllMethods } from "@dtax/tax-engine"; // optimizer
+import { analyzeHoldings } from "@dtax/tax-engine"; // portfolio
+
+// Parsers (auto-detect or use individually)
+import { parseCsv, detectCsvFormat } from "@dtax/tax-engine";
+```
+
+## 贡献
+
+欢迎贡献。请参阅 [CONTRIBUTING.md](CONTRIBUTING.md) 了解贡献指南。
+
+```bash
+# Prerequisites: Node.js >= 20, pnpm >= 9
+git clone https://github.com/dTaxLab/dtax.git && cd dtax
+pnpm install
+pnpm test # 800+ tests across all packages
+pnpm build # build all packages
+```
+
+## 许可证
+
+本仓库中的所有包均采用 [AGPL-3.0](LICENSE) 许可证。
+
+这意味着你可以在项目中自由使用 dTax。如果你修改源代码并分发(包括作为网络服务),你必须在 AGPL-3.0 下发布你的修改。
+
+商业许可咨询:[getdtax.com](https://getdtax.com)
+
+## 链接
+
+- 官网:[getdtax.com](https://getdtax.com)
+- 问题反馈:[GitHub Issues](https://github.com/dTaxLab/dtax/issues)
+- 讨论:[GitHub Discussions](https://github.com/dTaxLab/dtax/discussions)
From f8f72c65e71dff7a1b41b858bba4f1c448b4c43c Mon Sep 17 00:00:00 2001
From: namjar
Date: Sat, 14 Mar 2026 03:42:27 -0700
Subject: [PATCH 011/101] fix: include multilingual READMEs in public repo sync
+ fix logo path
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Add README.{zh,zh-Hant,es,ja,ko,pt}.md to sync-public.yml filter
- Copy logo to docs/logo.png (apps/ excluded from public repo)
- Update logo path in all 7 READMEs: apps/web/public/ → docs/
Co-Authored-By: Claude Opus 4.6
---
README.es.md | 2 +-
README.ja.md | 2 +-
README.ko.md | 2 +-
README.md | 2 +-
README.pt.md | 2 +-
README.zh-Hant.md | 2 +-
README.zh.md | 2 +-
docs/logo.png | Bin 0 -> 206481 bytes
8 files changed, 7 insertions(+), 7 deletions(-)
create mode 100644 docs/logo.png
diff --git a/README.es.md b/README.es.md
index f42b790d..4081db5b 100644
--- a/README.es.md
+++ b/README.es.md
@@ -1,5 +1,5 @@
-
+
dTax
El único motor completo de impuestos cripto en TypeScript disponible en npm
diff --git a/README.ja.md b/README.ja.md
index f77d14a2..efca24ce 100644
--- a/README.ja.md
+++ b/README.ja.md
@@ -1,5 +1,5 @@
-
+
dTax
npm で利用できる唯一の完全な TypeScript 暗号資産税務エンジン
diff --git a/README.ko.md b/README.ko.md
index 015036fa..e0e31299 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -1,5 +1,5 @@
-
+
dTax
npm에서 유일한 완전한 TypeScript 암호화폐 세금 엔진
diff --git a/README.md b/README.md
index 5529f28e..c58a3365 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
-
+
dTax
The only complete TypeScript crypto tax engine on npm
diff --git a/README.pt.md b/README.pt.md
index 4a55b5e4..37dee530 100644
--- a/README.pt.md
+++ b/README.pt.md
@@ -1,5 +1,5 @@
-
+
dTax
O único motor completo de impostos sobre criptomoedas em TypeScript no npm
diff --git a/README.zh-Hant.md b/README.zh-Hant.md
index f3a2dae4..5d5b2ccc 100644
--- a/README.zh-Hant.md
+++ b/README.zh-Hant.md
@@ -1,5 +1,5 @@
-
+
dTax
npm 上唯一完整的 TypeScript 加密貨幣稅務引擎
diff --git a/README.zh.md b/README.zh.md
index 0982fbd6..48461144 100644
--- a/README.zh.md
+++ b/README.zh.md
@@ -1,5 +1,5 @@
-
+
dTax
npm 上唯一完整的 TypeScript 加密货币税务引擎
diff --git a/docs/logo.png b/docs/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..1706ff588c644755e424e79cc00bb2b31284fdf2
GIT binary patch
literal 206481
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