ialbert 2023-05-19 19:23:50 +03:00
parent df63b37e3c
commit 6bdfb36c52
10 changed files with 473 additions and 3 deletions

View file

@ -0,0 +1,153 @@
package ru.spcex.clearing.session.stage.impl;
import ru.clearing.classes.statics.data.account.Account;
import ru.clearing.classes.statics.data.company.Company;
import ru.clearing.classes.statics.data.execution.ExecutionCommon;
import ru.clearing.classes.statics.data.execution.ExecutionDeposit;
import ru.clearing.classes.statics.data.execution.ExecutionFond;
import ru.clearing.classes.statics.data.misc.Market;
import ru.clearing.classes.statics.data.misc.Session;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.registry.TradingClearingRegistry;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.session.stage.util.RegistryUtil;
import ru.spcex.platform.classes.base.interfaces.ExecutionType;
import ru.spcex.platform.enumeration.*;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class RegistryBuilder {
public static RegistryBuilder builder() {
return new RegistryBuilder();
}
private RegistryBuilder() {
}
private Imdg<Company> companyImdg;
private Imdg<TradingClearingRegistry> tradingClearingRegistryImdg;
private Imdg<Account> accountImdg;
private Imdg<Market> marketImdg;
private Imdg<Session> sessionImdg;
private ExecutionCommon exec;
private RegistryDesignation regDsgn;
public RegistryBuilder imdg(ImdgProvider imdgProvider) {
this.companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class);
this.tradingClearingRegistryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class);
this.accountImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Account, Account.class);
this.marketImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Market, Market.class);
this.sessionImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Session, Session.class);
return this;
}
public RegistryBuilder exec(ExecutionCommon exec) {
this.exec = exec;
return this;
}
public RegistryBuilder registryDesignation(RegistryDesignation registryDesignation) {
this.regDsgn = registryDesignation;
return this;
}
public Registry build() {
ISide side = getSide(exec);
Registry reg = new Registry();
reg.setCompanyId(exec.getCompanyId());
Company company = searchCompany();
reg.setTradingCode(company.getTradingCode());
reg.setClearingCode(company.getClearingCode());
reg.setShortName(company.getShortName());
reg.setFullName(company.getFullName());
TradingClearingRegistry tcr = searchTradingClearingRegistry();
if ((regDsgn.equals(RegistryDesignation.O) && side.isBuy()) || (regDsgn.equals(RegistryDesignation.T) && side.isSell())) {
reg.setAccountId(tcr.getMoneyAccountId());
reg.setRegistryInstrumentType(RegistryInstrumentType.M.getKey());
reg.setBalanceDimension(BalanceDimension.MONY.getKey()); //fixme add second leg code branch
} else if ((regDsgn.equals(RegistryDesignation.T) && side.isBuy()) || (regDsgn.equals(RegistryDesignation.O) && side.isSell())) {
reg.setAccountId(tcr.getDepoAccountId());
reg.setRegistryInstrumentType(RegistryInstrumentType.S.getKey());
reg.setBalanceDimension(BalanceDimension.PICS.getKey()); //fixme add second leg code branch
}
Account account = accountImdg.getSingleObjectByID(reg.getAccountId());
reg.setAccountType(account.getAccountType());
reg.setAccount(account.getAccount());
reg.setRegistryDesignation(regDsgn.getKey());
reg.setRegistryCapacity(account.getAccountType());
reg.setRegistryUnit(RegistryUnit.T.getKey());
reg.setRegistryCode(RegistryUtil.clearingCode(reg));
reg.setTradingClearingRegistryId(exec.getTradingClearingRegistryId());
reg.setTradingClearingRegistry(tcr.getCode());
reg.setRegistryStatus(RegistryStatus.PROC.getKey());
reg.setSecurityId(exec.getSecurityId());
reg.setSecuritySymbol(exec.getSecuritySymbol());
switch (exec.type()) {
case ExecutionDeposit -> {
ExecutionDeposit execDep = (ExecutionDeposit) this.exec;
reg.setBalance((execDep).getFirstLegAmount());
if (regDsgn.equals(RegistryDesignation.T)) {
reg.setSettledCredit((execDep).getFirstLegAmount());
} else if (regDsgn.equals(RegistryDesignation.O)) {
reg.setSettledDebit((execDep).getFirstLegAmount());
}
reg.setSettlementDate(execDep.getFirstLegSettlementDate()); //fixme add second leg code branch
reg.setSettlementCode(execDep.getFirstLegSettlementCode()); //fixme add second leg code branch
reg.setRefundDate(execDep.getSecondLegSettlementDate()); //fixme add second leg code branch??
reg.setValueDate(execDep.getFirstLegSettlementDate());
reg.setContract(execDep.getContract());
}
case ExecutionFond -> {
ExecutionFond execFond = (ExecutionFond) this.exec;
reg.setBalance(execFond.getSettlementAmount());
reg.setSettlementDate(execFond.getSettlementDate());
reg.setSettlementCode(execFond.getSettlementCode());
}
}
reg.setTradingDate(exec.getTradingDate());
reg.setClearingDate(LocalDate.now());
reg.setPrice(exec.getPrice());
reg.setCounterPartyId(exec.getCounterPartyId());
reg.setGroupId(groupId());
reg.setSessionId(exec.getSessionId());
reg.setSessionType(sessionType());
return reg;
}
private static DateTimeFormatter yyyyMMdd = DateTimeFormatter.ofPattern("yyyyMMdd");
private Long groupId() {
LocalDate now = LocalDate.now();
Market market = marketImdg.getSingleObjectBySQL("code = '" + exec.getMarket() + "'");
return Long.valueOf(now.format(yyyyMMdd) + exec.getExchangeExecutionId() + market.getId());
}
//можно передать из стейджа
private String sessionType() {
Session session = sessionImdg.getSingleObjectByID(exec.getSessionId());
return session.getSessionType();
}
private Company searchCompany() {
return companyImdg.getSingleObjectBySQL("id = " + exec.getCompanyId());
}
private TradingClearingRegistry searchTradingClearingRegistry() {
return tradingClearingRegistryImdg.getSingleObjectBySQL("tradingClearingRegistryId = " + exec.getTradingClearingRegistryId());
}
private static ISide getSide(ExecutionCommon exec) {
if (exec.type().equals(ExecutionType.ExecutionDeposit)) {
return ISide.parse(MoneyFlowSide.class, exec.getSide());
} else if (exec.type().equals(ExecutionType.ExecutionFond)) {
return ISide.parse(Side.class, exec.getSide());
} else {
throw new RuntimeException("Unknown Execution type: " + exec.type());
}
}
}

View file

@ -0,0 +1,66 @@
package ru.spcex.clearing.session.stage.impl;
import ru.clearing.classes.statics.data.company.Company;
import ru.clearing.classes.statics.data.execution.ExecutionCommon;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.registry.TradingClearingRegistry;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
public class RegistryUpdater {
public static RegistryUpdater updater() {
return new RegistryUpdater();
}
private RegistryUpdater() {
}
private Imdg<Registry> registryImdg;
private Imdg<Company> companyImdg;
private Imdg<TradingClearingRegistry> tradingClearingRegistryImdg;
private ExecutionCommon exec;
private Registry rgs;
public RegistryUpdater imdg(ImdgProvider imdgProvider) {
this.registryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class);
this.companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class);
this.tradingClearingRegistryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class);
return this;
}
public RegistryUpdater registry(Registry rgs) {
this.rgs = rgs;
return this;
}
public RegistryUpdater exec(ExecutionCommon exec) {
this.exec = exec;
return this;
}
public Registry update() {
Registry reg = new Registry();
reg.setCompanyId(exec.getCompanyId());
Company company = searchCompany();
reg.setTradingCode(company.getTradingCode());
reg.setClearingCode(company.getClearingCode());
reg.setShortName(company.getShortName());
reg.setFullName(company.getFullName());
TradingClearingRegistry tcr = searchTradingClearingRegistry();
return null;
}
private Company searchCompany() {
return companyImdg.getSingleObjectBySQL("id = " + exec.getCompanyId());
}
private TradingClearingRegistry searchTradingClearingRegistry() {
return tradingClearingRegistryImdg.getSingleObjectBySQL("tradingClearingRegistryId = " + exec.getTradingClearingRegistryId());
}
// public
}

View file

@ -0,0 +1,180 @@
package ru.spcex.clearing.session.stage.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.clearing.classes.statics.data.execution.ExecutionCommon;
import ru.clearing.classes.statics.data.execution.ExecutionDeposit;
import ru.clearing.classes.statics.data.execution.ExecutionFond;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.models.RegistryTradingParams;
import ru.spcex.clearing.service.builder.sql.RegistryCodeSqlBuilder;
import ru.spcex.clearing.session.stage.ISessionStage;
import ru.spcex.clearing.session.stage.StageResult;
import ru.spcex.clearing.session.stage.Task;
import ru.spcex.clearing.session.stage.TaskType;
import ru.spcex.platform.classes.base.interfaces.ExecutionType;
import ru.spcex.platform.enumeration.*;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicate;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder;
import ru.spcex.platform.utils.collection.Pair;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiConsumer;
@Service
public class RequirmentsAndObligationCreation implements ISessionStage {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Imdg<Registry> registryImdg;
private final ImdgProvider imdgProvider;
@Autowired
public RequirmentsAndObligationCreation(ImdgProvider imdgProvider) {
this.registryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class);
this.imdgProvider = imdgProvider;
}
@SuppressWarnings("unchecked")
@Override
public StageResult<?> submit(Task<?> task) {
if (task.getTaskType() == TaskType.RequirementsAndObligationsCreate) {
return createRegisters((List<ExecutionCommon>) task.getData());
}
throw new IllegalStateException("Unknown task type: " + task.getTaskType());
}
private StageResult<?> createRegisters(List<ExecutionCommon> data) {
if (data.size() % 2 != 0) {
throw new IllegalStateException("Data size must be even (executions must match)");
}
//см. описание к #matchExecutions
for (int i = 0; i < data.size(); ) {
Pair<ExecutionCommon, ExecutionCommon> matched = matchExecutions(data, i);
if (matched == null) {
log.error("couldn't find match execution for id = {}", data.get(i).getId());
i += 1;
continue;
}
i += 2;
ExecutionCommon partyExec = matched.getFirst();
ExecutionCommon counterExec = matched.getSecond();
BiConsumer<ExecutionCommon, RegistryDesignation> findAndUpdateOrCreate = (exec, regDsgn) ->
findReg(exec, regDsgn).ifPresentOrElse(registry -> {
RegistryUpdater.updater()
.imdg(imdgProvider)
.exec(partyExec)
.registry(registry)
.update();
registryImdg.update(registry);
}, () -> {
Registry newRegister = RegistryBuilder.builder()
.imdg(imdgProvider)
.exec(partyExec)
.registryDesignation(regDsgn)
.build();
registryImdg.insert(newRegister);
});
findAndUpdateOrCreate.accept(partyExec, RegistryDesignation.O);
findAndUpdateOrCreate.accept(partyExec, RegistryDesignation.T);
findAndUpdateOrCreate.accept(counterExec, RegistryDesignation.O);
findAndUpdateOrCreate.accept(counterExec, RegistryDesignation.T);
}
return new StageResult<>(null, true);
}
private Optional<Registry> findReg(ExecutionCommon exec, RegistryDesignation des) {
ISide side = getSide(exec);
RegistryTradingParams p;
if (side.isBuy() && des.equals(RegistryDesignation.O)) {
p = new RegistryTradingParams(
RegistryDesignation.O, RegistryInstrumentType.M, null, RegistryUnit.T
);
} else if (side.isBuy() && des.equals(RegistryDesignation.T)) {
p = new RegistryTradingParams(
RegistryDesignation.T, RegistryInstrumentType.S, null, RegistryUnit.T
);
} else if (side.isSell() && des.equals(RegistryDesignation.O)) {
p = new RegistryTradingParams(
RegistryDesignation.O, RegistryInstrumentType.S, null, RegistryUnit.T
);
} else if (side.isSell() && des.equals(RegistryDesignation.T)) {
p = new RegistryTradingParams(
RegistryDesignation.T, RegistryInstrumentType.M, null, RegistryUnit.T
);
} else {
throw new IllegalStateException("cannot construct for " + des + " " + side);
}
String sql = RegistryCodeSqlBuilder.getInstance(p).build();
ImdgPredicateBuilder pb = registryImdg.predicateBuilder();
ImdgPredicate rgstrPredicate = pb.and(pb.sql(sql),
pb.equals("tradingClearingRegistryId", exec.getTradingClearingRegistryId()),
pb.equals("companyId", exec.getCompanyId()),
pb.equals("settlementDate", settlementDt(exec))
);
return Optional.ofNullable(registryImdg.getSingleObjectByPredicate(rgstrPredicate));
}
private LocalDate settlementDt(ExecutionCommon exec) {
if (exec.type().equals(ExecutionType.ExecutionDeposit)) {
return ((ExecutionDeposit) exec).getFirstLegSettlementDate();
} else if (exec.type().equals(ExecutionType.ExecutionFond)) {
return ((ExecutionFond) exec).getSettlementDate();
} else {
throw new RuntimeException("Unknown Execution type: " + exec.type());
}
}
/**
* входные данные отсортированы по exchangeExecutionId.
* сортировка выполнена на предыдущем шаге. (такое описание шагов в ТЗ)
* по идее, Execution с одинаковым exchangeExecutionId должно быть всего два.
* если это не так, передалать на коллекцию Long executionId в правильном порядке
* и для каждого искать мэтч отдельно в Imdg, с сохранением уже обработанных для избежания дублирования
*/
private Pair<ExecutionCommon, ExecutionCommon> matchExecutions(List<ExecutionCommon> data, int i) {
//if the last execution, then no match
if (i >= data.size() - 1) {
return null;
}
ExecutionCommon exec1 = data.get(i);
ExecutionCommon exec2 = data.get(i + 1);
//Встречная сделка контрагента выбирается из executionDeposit/Fond по условию:
//exchangeExecutionId=currentExecutionDeposit/Fond.exchangeExecutionId
//и [side=SELL (если currentExecutionDeposit/Fond.side=BUY) или side=BUY (если currentExecutionDeposit/Fond.side=SELL) по справочнику moneyFlowSide или справочнику side в зависимости от секции обрабатываемой сделки]
//и companyId=currentExecutionDeposit/Fond.counterPartyId
boolean valid = true;
if (!Objects.equals(exec1.getExchangeExecutionId(), exec2.getExchangeExecutionId())) {
valid = false;
} else if (Objects.equals(getSide(exec1), getSide(exec2))) {
valid = false;
} else if (!Objects.equals(exec1.getCounterPartyId(), exec2.getCounterPartyId())) {
valid = false;
}
if (!valid) {
log.error("FATAL skipping ExchangeExecutionId: {}", exec1.getId());
}
return new Pair<>(exec1, exec2);
}
private static ISide getSide(ExecutionCommon exec) {
if (exec.type().equals(ExecutionType.ExecutionDeposit)) {
return ISide.parse(MoneyFlowSide.class, exec.getSide());
} else if (exec.type().equals(ExecutionType.ExecutionFond)) {
return ISide.parse(Side.class, exec.getSide());
} else {
throw new RuntimeException("Unknown Execution type: " + exec.type());
}
}
}

View file

@ -0,0 +1,20 @@
package ru.spcex.platform.enumeration;
import ru.spcex.platform.utils.enumeration.IEnumKey;
public enum BalanceDimension implements IEnumKey {
MONY("MONY"),
PICS("PICS"),
;
private final String key;
BalanceDimension(String key) {
this.key = key;
}
@Override
public String getKey() {
return key;
}
}

View file

@ -0,0 +1,15 @@
package ru.spcex.platform.enumeration;
import ru.spcex.platform.utils.enumeration.IEnumKey;
public interface ISide {
boolean isBuy();
boolean isSell();
default boolean isOpposite(ISide other) {
return (isBuy() && other.isSell()) || (isSell() && other.isBuy());
}
static <T extends Enum<T> & IEnumKey & ISide> ISide parse(Class<T> clazz, String key) {
return IEnumKey.getEnumByKey(clazz, key);
}
}

View file

@ -2,7 +2,7 @@ package ru.spcex.platform.enumeration;
import ru.spcex.platform.utils.enumeration.IEnumKey;
public enum MoneyFlowSide implements IEnumKey {
public enum MoneyFlowSide implements IEnumKey, ISide {
BUY("BUY"),
SELL("SELL"),
;
@ -17,4 +17,15 @@ public enum MoneyFlowSide implements IEnumKey {
public String getKey() {
return key;
}
@Override
public boolean isBuy() {
return equals(BUY);
}
@Override
public boolean isSell() {
return equals(SELL);
}
}

View file

@ -3,7 +3,7 @@ package ru.spcex.platform.enumeration;
import ru.spcex.platform.utils.enumeration.IEnumKey;
public enum RegistryStatus implements IEnumKey {
OK("OK"),
OK("OK"), PROC("PROC"),
;
private final String key;

View file

@ -2,7 +2,7 @@ package ru.spcex.platform.enumeration;
import ru.spcex.platform.utils.enumeration.IEnumKey;
public enum Side implements IEnumKey {
public enum Side implements IEnumKey, ISide {
BUY("B"),
SELL("S"),
;
@ -17,4 +17,15 @@ public enum Side implements IEnumKey {
public String getKey() {
return key;
}
@Override
public boolean isBuy() {
return equals(BUY);
}
@Override
public boolean isSell() {
return equals(SELL);
}
}

View file

@ -251,6 +251,15 @@ public class ImdgHazelcast<T extends SpcexObjectBase> implements Imdg<T> {
return searchResult;
}
@Override
public T getSingleObjectByPredicate(ImdgPredicate predicate) {
Collection<T> els = getCollectionObjectsByPredicate(predicate);
if (els.size() > 1) {
throw new RuntimeException("More than one element found by predicate - " + els.size());
}
return els.iterator().next();
}
@Override
public ImdgPredicateBuilder predicateBuilder() {
return ImdgPredicateBuilderHazelcast.instance();

View file

@ -101,6 +101,11 @@ public interface Imdg<T extends SpcexObjectBase> {
throw new UnsupportedOperationException("not implemented getCollectionObjectsByPredicate");
}
// Predicate API
default T getSingleObjectByPredicate(ImdgPredicate predicate) {
throw new UnsupportedOperationException("not implemented getSingleObjectByPredicate");
}
default ImdgPredicateBuilder predicateBuilder() {
throw new UnsupportedOperationException("not implemented predicateBuilder API");
}