---
title: 'xtbrep — Analizator portfela XTB'
url: 'https://kvc.pl/repositories/xtbrep'
markdown: 'https://kvc.pl/repositories/xtbrep.md'
date: '2026-08-29'
description: 'XTBrep: polskojęzyczny analizator portfela XTB (xlsx z xStation). P/L, XIRR, PIT-38, dywidendy, inflacja GUS. WinAPI + C++23, bez zależności.'
---

> XTBrep: polskojęzyczny analizator portfela XTB (xlsx z xStation).

 [ Download xtbrep.zip](https://kvc.pl/repositories/xtbrep/xtbrep.zip) [ Download Source Code](https://kvc.pl/repositories/xtbrep/xtbrep-source.zip)

# xtbrep — Analizator portfela XTB

 [![Screenshot 1](https://kvc.pl/user/pages/04.repositories/17.xtbrep/images/xtb_01.jpg)](https://kvc.pl/user/pages/04.repositories/17.xtbrep/images/xtb_01.jpg) [![Screenshot 2](https://kvc.pl/user/pages/04.repositories/17.xtbrep/images/xtb_02.jpg)](https://kvc.pl/user/pages/04.repositories/17.xtbrep/images/xtb_02.jpg) [![Screenshot 3](https://kvc.pl/user/pages/04.repositories/17.xtbrep/images/xtb_03.jpg)](https://kvc.pl/user/pages/04.repositories/17.xtbrep/images/xtb_03.jpg)

 ![]()

**Version 1.1.0** — Desktopowy analizator portfela XTB w WinAPI + C++23 *Wczytuje eksport XLSX z xStation i liczy P/L, XIRR, PIT-38, dywidendy, odsetki oraz porównanie z inflacją GUS.* *Zero zależności, czysty WinAPI, statyczne `/MT`, brak VC++ Redistributable.* 

---

## Spis treści

- [Co robi program](#co-robi-program)
- [Pojęcia finansowe](#pojecia-finansowe)
- [Algorytm XIRR](#algorytm-xirr)
- [Architektura](#architektura)
- [Moduł A — Zestawienie po symbolu](#modul-a--zestawienie-po-symbolu)
- [Moduł B — Silnik podatkowy PIT-38](#modul-b--silnik-podatkowy-pit-38)
- [Moduł C — Dane inflacyjne CPI](#modul-c--dane-inflacyjne-cpi)
- [Format danych wejściowych](#format-danych-wejsciowych)
- [Pipeline parsowania XLSX](#pipeline-parsowania-xlsx)
- [Mapa modułów](#mapa-modulow)
- [Drzewo źródeł](#drzewo-zrodel)
- [Budowanie](#budowanie)
- [Wymagania systemowe](#wymagania-systemowe)
- [Eksport CSV i schowek](#eksport-csv-i-schowek)

---

## Co robi program

xtbrep wczytuje eksport XTB (menu xStation → Historia → Eksportuj jako XLSX) i w jednym oknie prezentuje:

| Zakładka | Zawartość |
|---|---|
| **Podsumowanie** | Wpłaty, wypłaty, net P&L zamknięty/otwarty, dywidendy, odsetki, XIRR, szacunek podatkowy |
| **Symbole** | Zestawienie zysku/straty i dywidend per ticker, sortowalne, z filtrem |
| **Transakcje** | Pełna lista zamkniętych pozycji z Gross P&L / prowizja / swap / Net P&L |
| **Przepływy gotówki** | Wszystkie operacje cashowe: wpłaty, wypłaty, dywidendy, podatki u źródła, odsetki |
| **Podatek PL** | Roczna tabela PIT-38: podstawa Belki, odliczenia strat, dopłata od dywidend |
| **Wykres** | Słupki roczne net P&L + linia kumulatywna + linia inflacyjna (benchmark CPI GUS) |

Filtr w pasku narzędzi działa na żywo na zakładkach Symbole, Transakcje i Przepływy. Dwuklik wiersza w Symbolach kopiuje ticker do filtra i przełącza na Transakcje — natychmiast widać całą historię danego waloru.

---

## Pojęcia finansowe

### P&L (Profit & Loss)

XTB rozróżnia:

- **Gross P&L** — wynik brutto transakcji bez kosztów transakcyjnych
- **Prowizja** — opłata brokera przy otwarciu/zamknięciu pozycji (wartość ujemna)
- **Swap** — koszt finansowania pozycji overnight (może być ujemny lub dodatni)
- **Net P&L** = Gross P&L + Prowizja + Swap

Program agreguje Net P&L na rok, po symbolu i łącznie.

### Otwarte vs zamknięte pozycje

Eksport XTB zawiera wyłącznie **zamknięte** pozycje. Wartość **otwartych pozycji** jest szacowana ze wzoru:

```
openNetPl = portfolioValueNow − netDeposited − closedNetPl
```

gdzie `portfolioValueNow` to aktualna wartość konta wpisana ręcznie przez użytkownika. Bez tego parametru pole otwarte wynosi zero.

### Dywidendy a podatek u źródła (Withholding Tax)

XTB w operacjach gotówkowych rejestruje dwa typy wierszy per dywidenda:

| Typ operacji | Znaczenie |
|---|---|
| `DIVIDENT` | Kwota brutto dywidendy uznana na rachunku |
| `Withholding Tax` | Podatek potrącony u źródła przez kraj emitenta (wartość ujemna) |

`dividendsNet = suma(DIVIDENT) + suma(Withholding Tax)` — wartość netto faktycznie wpływająca na konto.

### XIRR a zwykłe stopy zwrotu

Prosta stopa zwrotu `(wynik / zainwestowane)` jest myląca przy nieregularnych wpłatach w czasie — kwota zainwestowana zmienia się wraz z każdą wpłatą i wypłatą. XIRR eliminuje ten błąd przez sprowadzenie wszystkich przepływów do jednej rocznej stopy dyskontowej. Patrz sekcja [Algorytm XIRR](#algorytm-xirr).

---

## Algorytm XIRR

### Definicja

XIRR (Extended Internal Rate of Return) to annualizowana stopa zwrotu `r`, dla której wartość bieżąca netto (NPV) serii datowanych przepływów pieniężnych wynosi zero:

```
NPV(r) = Σ [ CF_i / (1 + r)^t_i ] = 0
```

gdzie:

- `CF_i` — przepływ pieniężny nr `i` (wpłata jako wartość **ujemna**, wypłata lub końcowa wartość portfela jako **dodatnia**)
- `t_i` — czas w latach od pierwszego przepływu do przepływu `i`, liczony jako `(JDN_i − JDN_0) / 365.25`
- `JDN` — Juliański Numer Dnia (Julian Day Number) — algebraicznie poprawna reprezentacja dat bez problemu przestępnych lat

### Przepływy gotówki używane w obliczeniu

flowchart LR A[Operacje gotowkowe XTB] --> B[Wektor CashFlow] C[Wartosc portfela teraz] --> B B --> D[XIRR Newton Raphson] D --> E[Stopa roczna] 

Wpłaty są traktowane jak wydatek inwestora (przepływ ujemny), wypłaty i końcowa wartość portfela jak zwrot (przepływ dodatni) — konwencja zgodna z Excelem i Google Sheets.

### Metoda bisekcji ze wstępnym bracketingiem

Implementacja używa bisekcji (nie Newton-Raphson) dla lepszej stabilności numerycznej przy dużych stopach:

```
1. Przedział [low=−0.999, high=1.0]
2. Rozszerzaj high×2, aż NPV(low)·NPV(high) < 0  (max high=100.0)
3. 120 iteracji bisekcji: mid = (low+high)/2
```

Implementacja (`Report.cpp::ComputeXirr`):

```cpp
double low = -0.999, high = 1.0;
double fLow = npv(low), fHigh = npv(high);
while (fLow * fHigh > 0.0 && high < 100.0) {
    high *= 2.0; fHigh = npv(high);
}
if (fLow * fHigh > 0.0) return 0.0;

double mid = 0.0;
for (int i = 0; i < 120; ++i) {
    mid = (low + high) * 0.5;
    const double fMid = npv(mid);
    if (std::abs(fMid) < 1e-10) break;
    if (fLow * fMid < 0.0) { high = mid; fHigh = fMid; }
    else                   { low  = mid; fLow  = fMid; }
}
return mid;
```

Ograniczenie `low ≥ −0.999` zapobiega singularności `(1+r)^t` przy ujemnych stawkach bliskich −1.

### XIRR całościowy vs. roczny

| Tryb | Przepływy | Wartość terminalna |
|---|---|---|
| **Cała historia** | wszystkie wpłaty/wypłaty od początku | bieżąca wartość portfela |
| **Rok X** (filtr roku) | saldo na 1 sty roku X + wpłaty w roku X | szacunkowa wartość na 31 gru roku X |

Dla filtra rocznego przepływy w `ComputeAnnualXirr()`:

```
t = 0,0 : −EstimatePortfolio(rok−1)   ← kapitał na początku roku
t = x   : −wpłata lub +wypłata
t = 1,0 : +EstimatePortfolio(rok)     ← wartość na końcu roku
```

### Dlaczego XIRR jest dokładniejszy niż IRR

| Właściwość | IRR | XIRR |
|---|---|---|
| Uwzględnia konkretne daty przepływów | nie (zakłada równe okresy) | tak |
| Poprawny dla nieregularnych wpłat | nie | tak |
| Annualizuje wynik | wynik per okres | zawsze roczny |
| Zgodny z Excelem/Google Sheets | tylko IRR | XIRR |
| Czas obliczeń na 500 przepływach | O(n·iter) | O(n·iter), ident. |

Dla portfela z wpłatami co kilka miesięcy przez kilka lat różnica między IRR a XIRR może wynosić kilka punktów procentowych — IRR zawyża stopę zwrotu przy częstych wpłatach na początku.

### Juliański Numer Dnia (JDN)

Daty transakcji z seriali Excela przeliczane są do JDN — liczby całkowitej liczącej dni od 1 stycznia 4713 p.n.e. (południe). Algorytm Meeus'a (`StringUtil.cpp::JulianDay`) jest algebraicznie dokładny dla dowolnej daty po roku 1582:

```cpp
int JulianDay(int y, int m, int d) noexcept {
    return d - 32075
         + 1461 * (y + 4800 + (m - 14) / 12) / 4
         +  367 * (m - 2 - (m - 14) / 12 * 12) / 12
         -    3 * ((y + 4900 + (m - 14) / 12) / 100) / 4;
}
```

Czas w latach: `t = (JDN_i − JDN_0) / 365.25`. Dzielnik `365.25` odpowiada średniej długości roku gregoriańskiego.

---

## Architektura

### Warstwy

flowchart TD UI[UI] --> Core[Core] Core --> IO[IO] Core --> Utils[Utils] UI --> CLI[CLI] App[App] --> UI App --> CLI UI -.->|WM_INFLATION_UPDATED| Core 

### Przepływ danych od pliku do ekranu

sequenceDiagram participant U as Uzytkownik participant D as MainDialog participant B as BuildReport participant X as XlsxReader participant T as TaxEngine participant F as InflationFetcher U->>D: otworz XLSX D->>B: BuildReport B->>X: Open X-->>B: Closed positions X-->>B: Cash operations B->>B: Parse i agreguj B->>T: BuildTaxReport T-->>B: TaxReport B-->>D: Report ok D->>D: Populate taby par Tlo F->>F: Fetch CPI F-->>D: WM_INFLATION_UPDATED D->>D: UpdateCpiData end 

---

## Moduł A — Zestawienie po symbolu

### Cel

Zakładka **Symbole** agreguje wynik per ticker przez cały okres historii. Kolumny:

| Symbol | Transakcje | Gross P/L | Prowizje | Swap | Net P/L | Dywidendy | Razem |
|---|---|---|---|---|---|---|---|

`Razem = Net P/L + Dywidendy` — faktyczny wkład instrumentu do portfela.

### Agregacja

Podczas `Aggregate()` w `Report.cpp` jednoczesny forward pass po `trades` i `cash`:

flowchart LR TR[Trades] --> SM[SymbolSummary mapa] CA[Cash] --> SM SM --> SV[Symbols wektor] 

Klucz mapy to `std::wstring` (ticker). Dywidendy i podatki u źródła dodają się algebraicznie: `dividends += amount` gdzie `Withholding Tax` ma wartość ujemną — wynik to automatycznie kwota netto bez osobnego odejmowania.

### Filtr live

Pole `IDC_FILTER` obsługuje zdarzenie `EN_CHANGE`. Przy każdej zmianie tekstu wywołanie `ContainsInsensitive()` filtruje wektor przed odświeżeniem ListView — bez ponownego parsowania danych. Filtr działa równolegle na zakładkach Symbole, Transakcje i Przepływy (wspólne pole tekstowe, stan zachowany przy przełączaniu zakładek).

Dwuklik na symbolu w tabeli Symbole → kopiuje ticker do filtra → `TabCtrl_SetCurSel(tab_, 2)` → `OnTabChange()` → widać przefiltrowane transakcje dla wybranego waloru.

---

## Moduł B — Silnik podatkowy PIT-38

### Podstawy prawne

- **Art. 30b ustawy o PIT** — 19% podatek od dochodów z odpłatnego zbycia papierów wartościowych (tzw. podatek Belki od giełdy)
- **Art. 9 ust. 3** — straty można odliczać przez 5 kolejnych lat podatkowych, przy czym w jednym roku można odliczyć nie więcej niż **50% straty z danego roku stratnego**
- **Dywidendy zagraniczne (§30a + UPO)** — Polska wymaga łącznie 19% od kwoty brutto. Podatek potrącony u źródła (np. 15% USA via W-8BEN) zalicza się na poczet polskiego — dopłata = `max(0, 19% × gross − |withheld|)`. Nadwyżka podatku zagranicznego ponad 19% **nie** jest zwracana.

### Algorytm rozliczenia strat — kolejka przeniesionych strat

flowchart TD YS[YearSummary] --> CHECK{Zysk?} CHECK -->|Nie| ADD[Dodaj strata] ADD --> EXPIRE[Usun po 5 latach] CHECK -->|Tak| APPLY[Zastosuj straty] APPLY --> TAX[Belka 19 procent] TAX --> NEXT[Nastepny rok] EXPIRE --> NEXT 

Slot `LossSlot` przechowuje zarówno `original` (do wyznaczenia cap 50%) jak i `remaining` (saldo do odliczenia). Po wyczerpaniu slotu jest usuwany z deque. Sloty starsze niż 5 lat wygasają na początku każdej iteracji przed zastosowaniem.

### Dopłata dywidendowa

Dla każdego roku:

```
divWithheld = Σ |Withholding Tax|   (wartości ujemne → bierzemy wartość bezwzględną)
divSupplement = max(0.0, 0.19 × divGross − divWithheld)
```

Logika bierze pod uwagę lata, w których wpłynęły dywidendy, nawet jeśli w tym roku nie było zamkniętych pozycji (brak wiersza w `YearSummary`).

### Widok tabeli Podatek PL

| Rok | P/L pozycji | Straty odlicz. | Podstawa | Belka 19% | Dyw. brutto | Potrącono | Dopłata dyw. | Łącznie |
|---|---|---|---|---|---|---|---|---|
| 2021 | 4 200,00 zł | — | 4 200,00 zł | 798,00 zł | 320,00 zł | 48,00 zł | 12,80 zł | 810,80 zł |
| 2022 | −1 800,00 zł | — | — | — | — | — | — | — |
| 2023 | 3 500,00 zł | 900,00 zł | 2 600,00 zł | 494,00 zł | — | — | — | 494,00 zł |

Wiersz `SUMA` i sekcja strat do przeniesienia widoczne na końcu tabeli.

---

## Moduł C — Dane inflacyjne CPI

### Skąd są dane o inflacji (dla użytkownika)

Program zaciąga inflację online i ma kilka fallbacków, żeby wykres zawsze działał nawet bez internetu:

1. Cache w `%APPDATA%\xtbrep\cpi_cache.txt` (jeśli cache ma ≤ 30 dni, używa cache; źródło w UI: `cache GUS <rok>`).
2. Fetch online (GUS BDL API) (pobiera z `bdl.stat.gov.pl`, zmienna 59; po sukcesie zapisuje do cache i używa online; źródło w UI: `GUS BDL <rok> (online)`).
3. Stary cache (gdy online nie działa, a cache istnieje; źródło w UI: `cache GUS <rok> (przestarzały)`).
4. Fallback wbudowany (gdy wszystko zawiedzie; tablica wbudowana w `InflationData.h`; źródło w UI: `wbudowana <rok>`; najnowszy rok wbudowany: 2025).

Łańcuch działania: cache świeży → online → cache stary → fallback.

### Strategia trójetapowa

flowchart LR START[Start] --> CT[Wbudowana CPI] START --> TH[Fetcher] TH --> CACHE{Cache swiezy} CACHE -->|Tak| POST[Update UI] CACHE -->|Nie| NET[Online GUS] NET -->|OK| WRITE[Zapis cache] WRITE --> POST NET -->|Fail| STALE{Stary cache} STALE -->|Tak| POST STALE -->|Nie| FAIL[Fallback wbudowany] 

Tablica wbudowana `kCpiFallback[]` i stała `kCpiFallbackLastYear` żyją wyłącznie w `InflationData.h` — są `inline constexpr`, dostępne w każdej jednostce kompilacji bez ODR, bez pliku `.cpp`.

### Format cache

Plik tekstowy `%APPDATA%\xtbrep\cpi_cache.txt`:

```
FETCHED=2025-11-15
2005=0.021000
2006=0.010000
...
2025=0.050000
```

Data `FETCHED` wyznacza świeżość. Przy odczycie: `difftime(now, cachedT) / 86400.0 ≤ 30`. Zapis: atomowy — najpierw do pliku tymczasowego, potem `MoveFileEx` (nie zaimplementowane w tej wersji — zapis bezpośredni; NTFS jest wystarczająco odporny dla danych statystycznych).

### GUS BDL API

Endpoint: `https://bdl.stat.gov.pl/api/v1/data/by-variable/59?format=json&unit-level=0&page-size=40&lang=pl`

Zmienna `59` = "Wskaźnik cen towarów i usług konsumpcyjnych ogółem, rok poprzedni = 100". Wartość `102.1` oznacza inflację `2.1%` → `rate = val/100 − 1`.

Timeouty WinHTTP ustawione na 8 sekund per fazę (connect / send / receive) — wątek tła nie blokuje UI.

### Aktualizacja wykresu przy przybyciu danych

`ChartView::UpdateCpiData()` → `RebuildBars()` przelicza inflacyjny benchmark od zera korzystając z nowej tablicy CPI:

```
inflRefAbs_{i} = inflRefAbs_{i-1} × (1 + CPI_i) + newDep_i × (1 + CPI_i / 2)
inflGain_i = inflRefAbs_i − cumDeposited_i
```

Konwencja połowy roku (`CPI/2` dla nowych depozytów) modeluje depozyty rozłożone równomiernie w ciągu roku. `barInputs_` (dane niezależne od CPI) są zachowane między `SetData()` a `UpdateCpiData()` — brak ponownego parsowania pliku.

Footer wykresu (dwuliniowy pasek) wyświetla:

**Linia 1 — stopy procentowe:**

| Metryka | Wzór | Interpretacja |
|---|---|---|
| **XIRR** | bisekcja NPV=0 | annualizowana stopa zwrotu |
| **Realna stopa** | `(1+XIRR)/(1+CPI)−1` | zysk w sile nabywczej |
| **Alfa vs CPI** | `XIRR − CPI` \[pp\] | o ile pp bijesz inflację |

**Linia 2 — wartości PLN:** zysk łączny / wkład netto / powyżej inflacji (PLN).

Źródło: `GUS BDL 2025 (online)` / `cache GUS 2024` / `wbudowana 2025`.

---

## Format danych wejściowych

Plik XLSX z xStation zawiera dwa arkusze:

### CLOSED POSITION HISTORY

| Position | Symbol | Type | Volume | Open time | Open price | Market price | Purchase value | SL | TP | Margin | Commission | Swap | Rollover | Gross P/L | Comment |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|

Kluczowe kolumny używane przez parser:

| Kolumna | Typ | Użycie |
|---|---|---|
| `Symbol` | tekst | ticker, klucz w zestawieniu po symbolu |
| `Type` | tekst | `BUY` / `SELL` |
| `Close time` | serial Excel | data zamknięcia — P/L przypisywane do roku zamknięcia |
| `Open time` | serial Excel | fallback gdy `Close time` brak |
| `Gross P/L` | liczba | wynik brutto |
| `Commission` | liczba | prowizja (ujemna) |
| `Swap` | liczba | swap nocny |

### CASH OPERATION HISTORY

| ID | Type | Time | Comment | Symbol | Amount |
|---|---|---|---|---|---|

Typy operacji rozpoznawane przez parser (dopasowanie case-insensitive, toleruje sufiksy):

| Typ (przykłady z eksportu XTB) | Znaczenie |
|---|---|
| `deposit` / `Deposit` | wpłata środków |
| `withdrawal` / `Withdrawal from 1844768` | wypłata środków — toleruje suffix z numerem konta |
| `DIVIDENT` | dywidenda brutto (literówka w eksporcie XTB) |
| `Withholding Tax` | podatek u źródła potrącony od dywidendy (ujemny) |
| `Free-funds Interest` / `Free-funds Interest 2024-01` | odsetki od wolnych środków |
| `Free-funds Interest Tax` / `Free-funds Interest Tax 2024-01` | podatek od odsetek (ujemny) |

---

## Pipeline parsowania XLSX

XLSX to ZIP z plikami XML. Cały stos I/O napisany od zera — zero zewnętrznych bibliotek.

flowchart LR XLSX[Plik XLSX] --> ZR[ZipReader] ZR --> INF[Inflate] INF --> XML[XlsxReader] XML --> ROWS[XlsxSheet] ROWS --> RP[Report] 

### Serial Excela → data

Excel przechowuje daty jako liczbę dni od 30.12.1899 (z historycznym błędem: Feb 29 1900 nie istnieje, Excel go ma). Korekcja:

```cpp
void ExcelSerialToDate(double serial, int& y, int& m, int& d) noexcept {
    int n = static_cast<int>(serial);
    if (n >= 60) n--;  // cofnięcie błędu Lotus 1-2-3 skopiowanego przez Excel
    const int jdn = n + 2415020;  // JDN dla 31.12.1899
    // algorytm Meeus JDN → Gregorian
    ...
}
```

### Skanowanie nagłówków

Parser nie zakłada stałej pozycji kolumn — skanuje pierwszy wiersz z daną nazwą metodą `FindHeaderRow()` + `MapColumns()`, budując mapę `wstring → int`. Odporne na różne wersje eksportu XTB (różne liczby metadanych powyżej tabeli).

---

## Mapa modułów

| Moduł | Plik | Odpowiedzialność |
|---|---|---|
| **App** | `App.cpp` | bootstrap: dark mode, COM, InitCommonControls, `MainDialog::Show()` |
| **Cli** | `Cli.cpp` | tryb CLI: `xtbrep.exe plik.xlsx [portfel]` → CSV na stdout |
| **StringUtil** | `StringUtil.cpp/.h` | `Utf8ToWide`, `FormatPln`, `FormatPct`, `FormatDate`, `JulianDay`, `ExcelSerialToDate` |
| **Inflate** | `Inflate.cpp/.h` | DEFLATE dekompresja RFC 1951, własna implementacja |
| **ZipReader** | `ZipReader.cpp/.h` | czytanie ZIP: centralny katalog, lokalne nagłówki, wyodrębnianie pliku po nazwie |
| **XlsxReader** | `XlsxReader.cpp/.h` | parsowanie XLSX XML: shared strings, worksheet, typy komórek, seriale dat |
| **Report** | `Report.cpp/.h` | `BuildReport()`: parsowanie obu arkuszy + `Aggregate()` + wywołanie `BuildTaxReport()` |
| **TaxEngine** | `TaxEngine.cpp` | `BuildTaxReport()`: Belka 19%, deque strat 5-letni, dopłata dywidendowa |
| **InflationData** | `InflationData.h` | `constexpr kCpiFallback[]`, `CpiFallbackForYear()`, `kCpiFallbackLastYear` |
| **InflationFetcher** | `InflationFetcher.cpp/.h` | wątek tła WinHTTP, cache plik, GUS BDL API, `PostMessage(WM_INFLATION_UPDATED)` |
| **ChartView** | `ChartView.cpp/.h` | okno GDI dziecka: słupki roczne, linia kumulatywna, linia inflacyjna, double-buffer |
| **MainDialog** | `MainDialog.cpp/.h` | główne okno: 6 zakładek, 5 ListView, filtr live, MICA/dark mode, CSV eksport |
| **DarkMode** | `DarkMode.h` | uxtheme undocumented API: `SetPreferredAppMode`, `AllowDarkModeForWindow` |
| **LinkerDeps** | `LinkerDeps.cpp` | `#pragma comment(lib, ...)` zamiast ręcznego linkowania w projekcie |

---

## Drzewo źródeł

```
xtbrep/
├── src/
│   ├── xtbrep.cpp              wWinMainCRTStartup → App::Run()
│   ├── xtbrep.rc               zasoby: dialog, version info (UTF-8, #pragma code_page)
│   ├── xtbrep.manifest         DPI-aware, Common Controls v6, Win10+ compatibility
│   ├── Resource.h              IDC_ / IDM_ / WM_USER stałe
│   ├── WinCommon.h             _WIN32_WINNT=0x0A00, WIN32_LEAN_AND_MEAN, NOMINMAX
│   ├── LinkerDeps.cpp          pragma comment lib
│   ├── app/
│   │   ├── App.cpp/.h          bootstrap aplikacji
│   ├── cli/
│   │   ├── Cli.cpp/.h          interfejs wiersza poleceń
│   ├── core/
│   │   ├── Report.h            struktury danych: TradeRow, CashRow, YearSummary,
│   │   │                       SymbolSummary, TaxYear, LossSlot, TaxReport, Report
│   │   ├── Report.cpp          BuildReport() + Aggregate()
│   │   ├── TaxEngine.cpp       BuildTaxReport(): PIT-38, carryforward, dywidendy
│   │   ├── InflationData.h     constexpr kCpiFallback[], CpiFallbackForYear()
│   │   ├── InflationFetcher.h/.cpp  async WinHTTP, cache, GUS BDL API
│   │   ├── StringUtil.h/.cpp   formatowanie, kodowanie, daty
│   │   ├── XlsxReader.h/.cpp   parser XLSX XML
│   │   ├── ZipReader.h/.cpp    parser ZIP
│   │   └── Inflate.h/.cpp      dekompresja DEFLATE
│   └── ui/
│       ├── MainDialog.h/.cpp   główne okno dialogowe
│       ├── ChartView.h/.cpp    wykres GDI
│       └── DarkMode.h          ciemny motyw WinAPI
├── test/
│   └── test.xlsx               przykładowy eksport XTB do testów
├── xtbrep.vcxproj              projekt MSVC v145, C++23, x64/x86
├── xtbrep.vcxproj.filters      filtry Solution Explorer
├── xtbrep.slnx                 plik solution
└── build.ps1                   skrypt MSBuild PowerShell
```

---

## Budowanie

### Wymagania

- Visual Studio 2022 lub 2026 z toolsetem MSVC v145
- Windows SDK 10.0
- PowerShell 5.1+

### Kompilacja

```powershell
.\build.ps1
```

Lub bezpośrednio przez MSBuild:

```powershell
msbuild xtbrep.vcxproj /p:Configuration=Release /p:Platform=x64
```

### Konfiguracje projektu

| Konfiguracja | Optymalizacja | CRT |
|---|---|---|
| Debug \| x64 | wyłączona | dynamiczny `/MDd` |
| Release \| x64 | `MaxSpeed` `/O2` | statyczny `/MT` |
| Debug \| Win32 | wyłączona | dynamiczny `/MDd` |
| Release \| Win32 | `MaxSpeed` `/O2` | statyczny `/MT` |

Flagi kompilatora stosowane we wszystkich konfiguracjach:

```
/utf-8              kodowanie źródła i wykonania = UTF-8
/W4                 wysoki poziom ostrzeżeń
/permissive-        ścisła zgodność ISO C++
/sdl                dodatkowe kontrole bezpieczeństwa
/std:c++latest      C++23 (std::format, std::ranges, std::expected)
/Zc:__cplusplus     __cplusplus odzwierciedla faktyczny standard
/Zc:preprocessor    nowy preprocestor zgodny z normą
```

Release `/MT` zapewnia brak zależności od redistribuowalnych pakietów VC++.

---

## Wymagania systemowe

| Składnik | Wymaganie |
|---|---|
| System operacyjny | Windows 10 lub nowszy (MICA wymaga Win11 22000+) |
| Architektura | x64 (projekt domyślny) lub Win32 |
| Zależności zewnętrzne | **brak** — wyłącznie WinAPI (comctl32, dwmapi, uxtheme, ole32, shell32, shlwapi, winhttp) |
| Plik wejściowy | eksport `.xlsx` z xStation XTB (Historia → Eksportuj) |
| Połączenie sieciowe | opcjonalne — wyłącznie do aktualizacji danych CPI z GUS BDL |

---

## Eksport CSV i schowek

Przycisk **Eksport CSV** eksportuje aktywną zakładkę (z uwzględnieniem aktywnego filtra) do pliku CSV:

- Separator: `;` (zgodny z polskim Excel)
- Kodowanie: UTF-8 z BOM (`\xFEFF`) — Excel otwiera bez konfiguracji
- Pola z `;` lub `"` są cytowane zgodnie z RFC 4180

Schowek Windows: prawy przycisk myszy na dowolnej liście → `Kopiuj zaznaczone` (tabulator jako separator) lub `Kopiuj wszystkie`. Podwójne kliknięcie wiersza = `Kopiuj zaznaczone` — z wyjątkiem zakładki Symbole, gdzie podwójne kliknięcie ustawia filtr.

---

## C++23 — wybrane zastosowania

| Cecha | Gdzie użyta |
|---|---|
| `std::format` / `std::wformat` | formatowanie dat, kwot, procentów w całym UI |
| `std::ranges::sort` / `min_element` / `max_element` | sortowanie transakcji, szukanie dat granicznych |
| `std::ranges::stable_sort` | sortowanie ListView z zachowaniem kolejności równych elementów |
| `std::fma` | obliczenie `inflRefAbs` w `RebuildBars()` bez utraty precyzji |
| Designated initializers | inicjalizacja struktur konfiguracyjnych |
| `[[nodiscard]]` | `BuildReport()`, `ComputeXirr()`, wszystkie helpery zwracające wynik |
| `constexpr` / `inline constexpr` | tablica CPI w `InflationData.h`, stałe zasobów |
| `std::atomic<bool>` | guard jednoinstancyjny wątku InflationFetcher |
| `std::thread::detach` | wątek tła InflationFetcher bez zarządzania cyklem życia |
| `std::deque<LossSlot>` | kolejka carryforward strat podatkowych |

---

*xtbrep v1.1.0 — C++23, WinAPI, zero zewnętrznych zależności.*
[kvc.pl](https://kvc.pl) | <marek@kvc.pl>

### Add a comment

---

## Navigation

- Parent: [Repositories](https://kvc.pl/repositories.md)
- Previous: [KiTTY Password Decryptor](https://kvc.pl/repositories/kittypassworddecryptor.md)
- Next: [UnderVolter](https://kvc.pl/repositories/undervolter.md)
