Skip to content

Commit

Permalink
Merge pull request #32 from vananiev/feature-derivatives-market
Browse files Browse the repository at this point in the history
Срочный рынок Уралсиб
  • Loading branch information
vananiev authored May 2, 2020
2 parents 05d2f8c + d825dda commit 83bf076
Show file tree
Hide file tree
Showing 20 changed files with 352 additions and 35 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</parent>
<groupId>ru.portfolio</groupId>
<artifactId>portfolio</artifactId>
<version>2020.4-SNAPSHOT</version>
<version>2020.4-beta1</version>
<name>portfolio</name>
<description>Investing Portfolio</description>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ protected Collection<RowType> parseTable() {
return parseTable(table);
} catch (Exception e) {
throw new RuntimeException("Ошибка при парсинге таблицы '" + this.tableName + "' " +
"в файле " + getReport().getPath().getFileName());
"в файле " + getReport().getPath().getFileName(), e);
}
}

Expand Down
44 changes: 44 additions & 0 deletions src/main/java/ru/portfolio/portfolio/parser/ExcelTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,17 @@ public Cell getCell(CellAddress address) {
return sheet.getRow(address.getRow()).getCell(address.getColumn());
}

/**
* @return return cell value or defaultValue if the cell is missing or the type does not match the expected
*/
public int getIntCellValueOrDefault(Row row, TableColumnDescription columnDescription, int defaultValue) {
try {
return getIntCellValue(row, columnDescription);
} catch (Exception e) {
return defaultValue;
}
}

public int getIntCellValue(Row row, TableColumnDescription columnDescription) {
return (int) getLongCellValue(row, columnDescription);
}
Expand All @@ -212,6 +223,17 @@ public long getIntCellValue(CellAddress address) {
return (int) getLongCellValue(address);
}

/**
* @return return cell value or defaultValue if the cell is missing or the type does not match the expected
*/
public long getLongCellValueOrDefault(Row row, TableColumnDescription columnDescription, long defaultValue) {
try {
return getLongCellValue(row, columnDescription);
} catch (Exception e) {
return defaultValue;
}
}

public long getLongCellValue(Row row, TableColumnDescription columnDescription) {
return ExcelTableHelper.getLongCellValue(getCell(row, columnDescription));
}
Expand All @@ -220,6 +242,17 @@ public long getLongCellValue(CellAddress address) {
return ExcelTableHelper.getLongCellValue(getCell(address));
}

/**
* @return return cell value or defaultValue if the cell is missing or the type does not match the expected
*/
public BigDecimal getCurrencyCellValueOrDefault(Row row, TableColumnDescription columnDescription, BigDecimal defaultValue) {
try {
return getCurrencyCellValue(row, columnDescription);
} catch (Exception e) {
return defaultValue;
}
}

public BigDecimal getCurrencyCellValue(Row row, TableColumnDescription columnDescription) {
return ExcelTableHelper.getCurrencyCellValue(getCell(row, columnDescription));
}
Expand All @@ -228,6 +261,17 @@ public BigDecimal getCurrencyCellValue(CellAddress address) {
return ExcelTableHelper.getCurrencyCellValue(getCell(address));
}

/**
* @return return cell value or defaultValue if the cell is missing or the type does not match the expected
*/
public String getStringCellValueOrDefault(Row row, TableColumnDescription columnDescription, String defaultValue) {
try {
return getStringCellValue(row, columnDescription);
} catch (Exception e) {
return defaultValue;
}
}

public String getStringCellValue(Row row, TableColumnDescription columnDescription) {
return ExcelTableHelper.getStringCellValue(getCell(row, columnDescription));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,6 @@ public static BigDecimal getCurrencyCellValue(Cell cell) {
}

public static String getStringCellValue(Cell cell) {
if (cell == null || cell.getCellType() == CellType.BLANK) {
return "";
}
return cell.getStringCellValue();
return (cell.getCellType() == CellType.BLANK) ? "" : cell.getStringCellValue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ protected void initializeIfNeed() {
}
}
} catch (Exception e) {
log.warn("Ошибка при парсинге файла {}", report.getPath().getFileName());
throw new RuntimeException("Ошибка при парсинге файла " + report.getPath().getFileName(), e);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Portfolio
* Copyright (C) 2020 Vitalii Ananev <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package ru.portfolio.portfolio.parser;

import org.apache.poi.ss.usermodel.Row;

public class OptionalTableColumn implements TableColumn {

public static TableColumn of(TableColumn column) {
return AnyOfTableColumn.of(column, TableColumn.NOCOLUMN);
}

private OptionalTableColumn() {
}

@Override
public int getColumnIndex(int firstColumnForSearch, Row... headerRows) {
return -1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ public void parse(ReportTableFactory reportTableFactory) {
derivativeTransactionTable.getData().forEach(storage::addTransaction);
derivativeCashFlowTable.getData().forEach(c -> {
if (storage.addSecurity(c.getIsin())) {
if (c.getCount() == null &&
c.getEventType() == CashFlowType.DERIVATIVE_PROFIT) { // count is optional for derivatives
c = c.toBuilder().count(0).build();
}
storage.addSecurityEventCashFlow(c);
}
});
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/ru/portfolio/portfolio/parser/TableColumn.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
import org.apache.poi.ss.usermodel.Row;

public interface TableColumn {
TableColumn NOCOLUMN = (i, j) -> -1;

default TableColumn ofOptional(TableColumn column) {
return AnyOfTableColumn.of(column, TableColumn.NOCOLUMN);
}

/**
* @param headerRows header rows
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ private static String getPortfolio(Sheet sheet) {
}
}
throw new IllegalArgumentException(
"В отчете не найден номер договора по заданному шаблону '" + PORTFOLIO_MARKER + ": XXX'");
"В отчете не найден номер договора по заданному шаблону '" + PORTFOLIO_MARKER + " XXX'");
} catch (Exception e) {
throw new RuntimeException("Ошибка поиска номера Брокерского счета в отчете");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Portfolio
* Copyright (C) 2020 Vitalii Ananev <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package ru.portfolio.portfolio.parser.uralsib;

import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Row;
import ru.portfolio.portfolio.parser.AbstractReportTable;
import ru.portfolio.portfolio.parser.ExcelTable;
import ru.portfolio.portfolio.pojo.CashFlowType;
import ru.portfolio.portfolio.pojo.SecurityEventCashFlow;

import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static ru.portfolio.portfolio.parser.uralsib.PaymentsTable.PaymentsTableHeader.*;

@Slf4j
public class DerivativeCashFlowTable extends AbstractReportTable<SecurityEventCashFlow> {

private final Pattern contractPattern = Pattern.compile(".*\\sвариационной маржи по\\s(.+)$");

public DerivativeCashFlowTable(UralsibBrokerReport report) {
super(report, PaymentsTable.TABLE_NAME, "", PaymentsTable.PaymentsTableHeader.class);
}

protected Collection<SecurityEventCashFlow> getRow(ExcelTable table, Row row) {
String action = table.getStringCellValue(row, OPERATION);
action = String.valueOf(action).toLowerCase().trim();
if (!action.equalsIgnoreCase("вариационная маржа")) {
return emptyList();
}
return singletonList(SecurityEventCashFlow.builder()
.timestamp(convertToInstant(table.getStringCellValue(row, DATE)))
.portfolio(getReport().getPortfolio())
.value(table.getCurrencyCellValue(row, VALUE))
.currency(UralsibBrokerReport.convertToCurrency(table.getStringCellValue(row, CURRENCY)))
.eventType(CashFlowType.DERIVATIVE_PROFIT)
.isin(getContract(table, row))
.build());
}

private String getContract(ExcelTable table, Row row) {
String description = table.getStringCellValue(row, DESCRIPTION);
Matcher matcher = contractPattern.matcher(description);
if (matcher.find()) {
return matcher.group(1);
}
throw new RuntimeException("Не могу найти наименование контракта в отчете брокера по событию:" + description);
}

@Override
protected boolean checkEquality(SecurityEventCashFlow cash1, SecurityEventCashFlow cash2) {
return SecurityEventCashFlow.checkEquality(cash1, cash2);
}

@Override
protected Collection<SecurityEventCashFlow> mergeDuplicates(SecurityEventCashFlow oldObject, SecurityEventCashFlow newObject) {
return SecurityEventCashFlow.mergeDuplicates(oldObject, newObject);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Portfolio
* Copyright (C) 2020 Vitalii Ananev <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package ru.portfolio.portfolio.parser.uralsib;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Row;
import ru.portfolio.portfolio.parser.*;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static java.util.Collections.emptyList;
import static ru.portfolio.portfolio.parser.uralsib.DerivativeTransactionTable.FortsTableHeader.*;

@Slf4j
public class DerivativeTransactionTable extends AbstractReportTable<DerivativeTransaction> {
private static final String TABLE_NAME = "СДЕЛКИ С ФЬЮЧЕРСАМИ И ОПЦИОНАМИ";
private static final String TABLE_END_TEXT = "ДВИЖЕНИЕ ДЕНЕЖНЫХ СРЕДСТВ ЗА ОТЧЕТНЫЙ ПЕРИОД";

public DerivativeTransactionTable(UralsibBrokerReport report) {
super(report, TABLE_NAME, TABLE_END_TEXT, FortsTableHeader.class, 2);
}

@Override
protected Collection<DerivativeTransaction> getRow(ExcelTable table, Row row) {
Long transactionId = SecurityTransactionTable.getTransactionId(table, row, TRANSACTION);
if (transactionId == null) return emptyList();

boolean isBuy = table.getStringCellValue(row, DIRECTION).equalsIgnoreCase("покупка");
int count = table.getIntCellValue(row, COUNT);
BigDecimal valueInPoints = table.getCurrencyCellValue(row, QUOTE).multiply(BigDecimal.valueOf(count));
BigDecimal value = table.getCurrencyCellValue(row, VALUE);
String valueCurrency = UralsibBrokerReport.convertToCurrency(
table.getStringCellValueOrDefault(row, VALUE_CURRENCY, "RUB"));
if (isBuy) {
value = value.negate();
valueInPoints = valueInPoints.negate();
}
BigDecimal commission = table.getCurrencyCellValue(row, MARKET_COMMISSION)
.add(table.getCurrencyCellValue(row, BROKER_COMMISSION))
.negate();
List<DerivativeTransaction> transactionInfo = new ArrayList<>(2);
DerivativeTransaction.DerivativeTransactionBuilder builder = DerivativeTransaction.builder()
.timestamp(convertToInstant(table.getStringCellValue(row, DATE_TIME)))
.transactionId(transactionId)
.portfolio(getReport().getPortfolio())
.contract(table.getStringCellValue(row, CONTRACT))
.count((isBuy ? 1 : -1) * count);
transactionInfo.add(builder
.value(value)
.commission(commission)
.valueCurrency(valueCurrency)
.commissionCurrency("RUB") // FORTS, only RUB
.build());
transactionInfo.add(builder
.value(valueInPoints)
.commission(BigDecimal.ZERO)
.valueCurrency(DerivativeTransaction.QUOTE_CURRENCY)
.commissionCurrency("RUB") // FORTS, only RUB
.build());
return transactionInfo;
}

@RequiredArgsConstructor
enum FortsTableHeader implements TableColumnDescription {
DATE_TIME("дата расчетов"),
TRANSACTION("номер сделки"),
TYPE("вид контракта"),
CONTRACT("наименование контракта"),
DIRECTION("вид сделки"),
COUNT("количество контрактов"),
QUOTE(AnyOfTableColumn.of(
TableColumnImpl.of("цена фьючерса"),
TableColumnImpl.of("премия по опциону"))),
VALUE("^сумма$"),
VALUE_CURRENCY(OptionalTableColumn.of(
TableColumnImpl.of("валюта суммы"))), // sometime does not exists
MARKET_COMMISSION("комиссия тс"),
BROKER_COMMISSION("комиссия брокера");

@Getter
private final TableColumn column;
FortsTableHeader(String ... words) {
this.column = TableColumnImpl.of(words);
}
}
}
Loading

0 comments on commit 83bf076

Please sign in to comment.