Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Релиз 2022.9.1 #528

Merged
merged 10 commits into from
Feb 19, 2023
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ target/
!**/src/main/**
!**/src/test/**
/portfolio.log
report-backups/
investbook/

### STS ###
.apt_generated
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</parent>
<groupId>ru.investbook</groupId>
<artifactId>investbook</artifactId>
<version>2022.9</version>
<version>2022.9.1</version>

<name>investbook</name>
<description>Investor Accounting Book</description>
Expand Down Expand Up @@ -62,7 +62,7 @@

<properties>
<!-- Valid version is (0-255).(0-255).(0-65535) -->
<win.msi.version>22.9</win.msi.version>
<win.msi.version>22.9.1</win.msi.version>
<java.version>18</java.version>
<!-- version 3.2.0 provided by Spring Boot 2.4.1 bugged, using version from Spring Boot 2.3.4
https://stackoverflow.com/questions/65910112/maven-clean-install-failed-to-execute-goal-org-apache-maven-pluginsmaven-resou
Expand Down
10 changes: 10 additions & 0 deletions src/main/asciidoc/portfolio-status.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ NOTE: Отображается консолидированный отчет, о
по акции выполнен сплит 4:1, котировка снижена вчетверо до $110, на счете появились еще 3 акции. Усредненная цена
будет равна $25 и будет отражать усредненную цену покупки акций в новых ценах (после сплита).

+
Если часть позиций закрыта в профит, то усредненная цена может быть отрицательной величиной. Например,
если куплены 2 бумаги по $100, то при последующей продаже 1 бумаги по $250 усредненная цена составит -$50, показывая,
что на покупку бумаг собственных средств затрачено не было (они отбиты продажей одной из бумаг).

[#average-accrued-interest]
Усредненный НКД::
. Если по облигации еще не получены купоны, то это НКД, при которой нужно продать облигации, чтобы купонный доход был
Expand All @@ -101,6 +106,11 @@ NOTE: Отображается консолидированный отчет, о
Например, при продаже за 1030 рублей (НКД = 30 рублей) финансовый результат равен 60 рублей и удержано налогов с 60 рублей
(50 рублей с купона и 10 рублей с разницы курсов продажи-покупки).

+
Усредненный НКД может быть отрицательной величиной. Например, если куплены 2 облигации с НКД по 10 рублей, и потом
1 облигация продана с НКД 25 рублей, то усредненный НКД составит -5 рублей, показывая, что средства затраченные
на оплату НКД при покупке облигации отбиты полностью.

+
Отображается по купонам, полученным до заданной даты включительно (или за выбранный период),
только если у инструмента <<count,Изменение позиции>> не равно нулю
Expand Down
14 changes: 11 additions & 3 deletions src/main/java/ru/investbook/parser/uralsib/CashFlowTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ protected EventCashFlow parseRow(TableRow row) {
String description = row.getStringCellValueOrDefault(DESCRIPTION, "");
CashFlowType type;
switch (action) {
case"ввод дс":
case"вывод дс":
case "ввод дс":
case "вывод дс":
type = CashFlowType.CASH;
break;
case "перевод дс":
Expand All @@ -60,7 +60,7 @@ protected EventCashFlow parseRow(TableRow row) {
String to = matcherTo.group(1);
String from = matcherFrom.group(1);
if (isCurrentPortfolioAccount(to) != isCurrentPortfolioAccount(from)) {
if (getClientCode(from) != 0 && getClientCode(to) != 0) {
if (isExternalAccount(from) && isExternalAccount(to)) {
type = CashFlowType.CASH;
break;
}
Expand Down Expand Up @@ -100,6 +100,14 @@ private boolean isCurrentPortfolioAccount(String account) {
}
}

private boolean isExternalAccount(String account) {
try {
return getClientCode(account) != 0;
} catch (Exception ignore) {
return true; // current account != "00000[^0-9]*"
}
}

private Integer getClientCode(String account) {
try {
Matcher matcher = clientCodePattern.matcher(account);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@

public class DerivativeExpirationTable extends DerivativeTransactionTable {
static final String TABLE_NAME = "ИСПОЛНЕНИЕ КОНТРАКТОВ";
private static final String TABLE_END_TEXT = PaymentsTable.TABLE_NAME;

protected DerivativeExpirationTable(UralsibBrokerReport report) {
super(report, TABLE_NAME, TABLE_END_TEXT, 1);
super(report, TABLE_NAME, 1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,27 @@
import ru.investbook.parser.SingleAbstractReportTable;

import java.math.BigDecimal;
import java.util.regex.Pattern;

import static java.util.regex.Pattern.UNICODE_CHARACTER_CLASS;
import static ru.investbook.parser.uralsib.DerivativeTransactionTable.FortsTableHeader.*;

@Slf4j
public class DerivativeTransactionTable extends SingleAbstractReportTable<DerivativeTransaction> {
private static final String TABLE_NAME = "СДЕЛКИ С ФЬЮЧЕРСАМИ И ОПЦИОНАМИ";
private static final String TABLE_END_TEXT = PaymentsTable.TABLE_NAME;
private static final Pattern tableEndPredicate = Pattern.compile("^[А-Я\s]+$", UNICODE_CHARACTER_CLASS);
private boolean expirationTableReached = false;

public DerivativeTransactionTable(UralsibBrokerReport report) {
this(report, TABLE_NAME, TABLE_END_TEXT, 2);
this(report, TABLE_NAME, 2);
}

protected DerivativeTransactionTable(UralsibBrokerReport report, String tableName, String tableFooter, int headersRowCount) {
super(report, tableName, tableFooter, FortsTableHeader.class, headersRowCount);
protected DerivativeTransactionTable(UralsibBrokerReport report, String tableName, int headersRowCount) {
super(report,
(cell) -> cell.startsWith(tableName),
(cell) -> tableEndPredicate.matcher(cell).matches(),
FortsTableHeader.class,
headersRowCount);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* InvestBook
* Copyright (C) 2023 Spacious Team <spacious-team@ya.ru>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package ru.investbook.parser.uralsib;

import ru.investbook.parser.TransactionValueAndFeeParser;

public class RepoTransactionTable extends SecurityTransactionTable {
private static final String TABLE_NAME = "Специальные сделки РЕПО для переноса длинной позиции";

protected RepoTransactionTable(UralsibBrokerReport report,
SecuritiesTable securitiesTable,
ForeignExchangeRateTable foreignExchangeRateTable,
TransactionValueAndFeeParser transactionValueAndFeeParser) {
super(report, TABLE_NAME, securitiesTable, foreignExchangeRateTable, transactionValueAndFeeParser);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,15 @@ public SecurityTransactionTable(UralsibBrokerReport report,
SecuritiesTable securitiesTable,
ForeignExchangeRateTable foreignExchangeRateTable,
TransactionValueAndFeeParser transactionValueAndFeeParser) {
super(report, TABLE_NAME, "", TransactionTableHeader.class, 2);
this(report, TABLE_NAME, securitiesTable, foreignExchangeRateTable, transactionValueAndFeeParser);
}

protected SecurityTransactionTable(UralsibBrokerReport report,
String tableName,
SecuritiesTable securitiesTable,
ForeignExchangeRateTable foreignExchangeRateTable,
TransactionValueAndFeeParser transactionValueAndFeeParser) {
super(report, tableName, "", TransactionTableHeader.class, 2);
this.securitiesTable = securitiesTable;
this.foreignExchangeRateTable = foreignExchangeRateTable;
this.transactionValueAndFeeParser = transactionValueAndFeeParser;
Expand Down Expand Up @@ -128,7 +136,12 @@ private Security getSecurity(TableRow row) {
}

enum TransactionTableHeader implements TableColumnDescription {
DATE_TIME("дата", "поставки"),
DATE_TIME(
AnyOfTableColumn.of(
MultiLineTableColumn.of( // таблица "Специальные сделки РЕПО для переноса длинной позиции"
TableColumnImpl.of("дата", "поставки"),
TableColumnImpl.of("плановая")),
TableColumnImpl.of("дата", "поставки"))), // таблица "Биржевые сделки с ценными бумагами в отчетном периоде"
TRADE_ID("номер сделки"),
DIRECTION("вид", "сделки"),
COUNT("количество", "цб"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@
public class UralsibBrokerReport extends AbstractExcelBrokerReport {
// "УРАЛСИБ Брокер" или "УРАЛСИБ Кэпитал - Финансовые услуги" (старый формат 2018 г)
private static final String PORTFOLIO_MARKER = "Номер счета Клиента:";
private static final Predicate<Object> uralsibReportPredicate = (cell) ->
private final Predicate<Object> uralsibReportPredicate = (cell) ->
(cell instanceof String) && ((String) cell).contains("УРАЛСИБ");
private static final Predicate<Object> dateMarkerPredicate = (cell) ->
private final Predicate<Object> dateMarkerPredicate = (cell) ->
(cell instanceof String) && ((String) cell).contains("за период");
private final Workbook book;

Expand Down Expand Up @@ -78,7 +78,7 @@ public UralsibBrokerReport(String excelFileName, InputStream is, SecurityRegistr
setReportEndDateTime(getReportEndDateTime(reportPage));
}

private static void checkReportFormat(Path path, ReportPage reportPage) {
private void checkReportFormat(Path path, ReportPage reportPage) {
if (reportPage.find(0, 1, uralsibReportPredicate) == TableCellAddress.NOT_FOUND) {
throw new RuntimeException("В файле " + path + " не содержится отчет брокера Уралсиб");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public UralsibReportTables(UralsibBrokerReport report,
this.portfolioSecuritiesTable = new SecuritiesTable(report);
this.securityTransactionTable = WrappingReportTable.of(
new SecurityTransactionTable(report, portfolioSecuritiesTable, foreignExchangeRateTable, transactionValueAndFeeParser),
new RepoTransactionTable(report, portfolioSecuritiesTable, foreignExchangeRateTable, transactionValueAndFeeParser),
new SecurityDepositAndWithdrawalTable(report, portfolioSecuritiesTable));
this.couponAmortizationRedemptionTable =
new CouponAmortizationRedemptionTable(report, portfolioSecuritiesTable, securityTransactionTable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,9 @@ private Table.Record getSecurityStatus(Collection<String> portfolios, Security s
(securityType.isBond() ? ("+" + AMORTIZATION.getCellAddr()) : ""));
} else {
row.put(AVERAGE_PRICE, securityProfitService.getPurchaseCost(security, positions, toCurrency)
.abs()
.divide(BigDecimal.valueOf(Math.max(1, Math.abs(count))), 6, RoundingMode.CEILING));
.divide(BigDecimal.valueOf(-count), 6, RoundingMode.CEILING));
row.put(AVERAGE_ACCRUED_INTEREST, securityProfitService.getPurchaseAccruedInterest(security, positions, toCurrency)
.abs()
.divide(BigDecimal.valueOf(Math.max(1, Math.abs(count))), 6, RoundingMode.CEILING));
.divide(BigDecimal.valueOf(-count), 6, RoundingMode.CEILING));

quote = securityProfitService.getSecurityQuote(security, toCurrency, filter.getToDate());

Expand Down
9 changes: 5 additions & 4 deletions src/main/java/ru/investbook/upgrade/SqlDataExporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,24 @@
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class SqlDataExporter {
private static final String EXPECTED_INVESTBOOK_VERSION_FOR_EXPORT = "2022.9";
private static final String EXPORT_FILE_NAME = "export-2022.9.sql";
private final List<String> expectedInvestbookVersionsForExport = List.of("2022.9", "2022.9.1");
private final BuildProperties buildProperties;
private final InvestbookProperties investbookProperties;
private final JdbcTemplate jdbcTemplate;

@PreDestroy
public void preDestroy() {
String version = buildProperties.getVersion();
if (Objects.equals(version, EXPECTED_INVESTBOOK_VERSION_FOR_EXPORT)) {
if (expectedInvestbookVersionsForExport.contains(version)) {
Path file = investbookProperties.getDataPath()
.resolve("export-" + version + ".sql")
.resolve(EXPORT_FILE_NAME)
.toAbsolutePath();
exportSqlData(file);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application-dev.properties
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,4 @@ logging.file.name = ""
#spring.jackson.serialization.fail-on-empty-beans=false

investbook.report-backup = false
investbook.report-backup-path = report-backups
investbook.data-path = ./investbook