This repository has been archived by the owner. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
913 additions
and
420 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/main/java/ru/unclesema/ttb/config/ExceptionResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package ru.unclesema.ttb.config; | ||
|
||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.servlet.HandlerExceptionResolver; | ||
import org.springframework.web.servlet.ModelAndView; | ||
|
||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import java.util.Map; | ||
|
||
@Component | ||
public class ExceptionResolver implements HandlerExceptionResolver { | ||
@Override | ||
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { | ||
return new ModelAndView("error-page", Map.of("msg", ex.getMessage())); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
271 changes: 16 additions & 255 deletions
271
src/main/java/ru/unclesema/ttb/service/ApplicationService.java
Large diffs are not rendered by default.
Oops, something went wrong.
323 changes: 323 additions & 0 deletions
323
src/main/java/ru/unclesema/ttb/service/ApplicationServiceImpl.java
Large diffs are not rendered by default.
Oops, something went wrong.
35 changes: 35 additions & 0 deletions
35
src/main/java/ru/unclesema/ttb/service/analyze/AnalyzeService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package ru.unclesema.ttb.service.analyze; | ||
|
||
import ru.tinkoff.piapi.contract.v1.Candle; | ||
import ru.tinkoff.piapi.contract.v1.LastPrice; | ||
import ru.tinkoff.piapi.contract.v1.Operation; | ||
import ru.tinkoff.piapi.contract.v1.OrderDirection; | ||
import ru.unclesema.ttb.model.User; | ||
|
||
import java.math.BigDecimal; | ||
import java.util.List; | ||
|
||
/** | ||
* Сервис, отвечающий за режим анализа. | ||
*/ | ||
public interface AnalyzeService { | ||
/** | ||
* Метод добавляет новую операцию, совершенную во время симуляции работы стратегии | ||
*/ | ||
void processOrder(User user, String figi, long quantity, BigDecimal price, OrderDirection direction); | ||
|
||
/** | ||
* @return Совершённые стратегией операции | ||
*/ | ||
List<Operation> getOperations(User user); | ||
|
||
/** | ||
* @return Цену последней добавленной свечи | ||
*/ | ||
LastPrice getLastPrice(User user, String figi); | ||
|
||
/** | ||
* Добавить новую свечу | ||
*/ | ||
void processNewCandle(User user, Candle candle); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
163 changes: 39 additions & 124 deletions
163
src/main/java/ru/unclesema/ttb/service/front/FrontService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,190 +1,105 @@ | ||
package ru.unclesema.ttb.service.front; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Service; | ||
import ru.tinkoff.piapi.contract.v1.Instrument; | ||
import ru.tinkoff.piapi.contract.v1.MoneyValue; | ||
import ru.tinkoff.piapi.contract.v1.Operation; | ||
import ru.tinkoff.piapi.contract.v1.OperationType; | ||
import ru.unclesema.ttb.client.InvestClient; | ||
import ru.unclesema.ttb.model.User; | ||
import ru.unclesema.ttb.service.PriceService; | ||
import ru.unclesema.ttb.service.UserService; | ||
import ru.unclesema.ttb.strategy.CandleStrategy; | ||
import ru.unclesema.ttb.strategy.Strategy; | ||
import ru.unclesema.ttb.utility.Utility; | ||
|
||
import java.math.BigDecimal; | ||
import java.time.LocalDateTime; | ||
import java.time.ZoneId; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* Сервис для работы с UI | ||
*/ | ||
@Service | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class FrontService { | ||
private final List<Strategy> availableStrategies; | ||
private final List<CandleStrategy> availableCandleStrategies; | ||
private final PriceService priceService; | ||
private final UserService userService; | ||
private final InvestClient investClient; | ||
|
||
public interface FrontService { | ||
/** | ||
* @return всех существующих пользователей | ||
*/ | ||
public List<User> getAllUsers() { | ||
return userService.getAllUsers(); | ||
} | ||
List<User> getAllUsers(); | ||
|
||
/** | ||
* @return пользователь с заданным accountId | ||
* @throws IllegalArgumentException, если пользователь не найден | ||
*/ | ||
public User findUser(String accountId) { | ||
return userService.findUserByAccountId(accountId); | ||
} | ||
User findUser(String accountId); | ||
|
||
/** | ||
* @return имя заданного инструмента | ||
*/ | ||
public String getInstrumentName(User user, String figi) { | ||
return getInstrument(user, figi).getName(); | ||
} | ||
String getInstrumentName(User user, String figi); | ||
|
||
/** | ||
* @return заданный инструмент | ||
*/ | ||
public Instrument getInstrument(User user, String figi) { | ||
return investClient.getInstrument(user, figi); | ||
} | ||
Instrument getInstrument(User user, String figi); | ||
|
||
/** | ||
* Конвертирует <code>LastPrice</code> в <code>String</code> | ||
*/ | ||
public String lastPriceToString(User user, BigDecimal quantity, String figi) { | ||
String currency = getInstrument(user, figi).getCurrency(); | ||
BigDecimal lastPrice = priceService.getLastPrice(user, figi); | ||
return lastPrice.multiply(quantity).doubleValue() + " " + currency.toUpperCase(); | ||
} | ||
String lastPriceToString(User user, BigDecimal quantity, String figi); | ||
|
||
/** | ||
* Конвертирует <code>MoneyValue</code> в <code>String</code> | ||
*/ | ||
public String moneyValueToString(User user, String figi, MoneyValue value) { | ||
Instrument instrument = getInstrument(user, figi); | ||
return Utility.toBigDecimal(value).doubleValue() + " " + instrument.getCurrency().toUpperCase(); | ||
} | ||
String moneyValueToString(User user, String figi, MoneyValue value); | ||
|
||
/** | ||
* @return отчёт по стратегии | ||
*/ | ||
public StrategyStatement getStatement(User user) { | ||
Map<String, BigDecimal> benefitByCurrency = new HashMap<>(); | ||
// Обрабатываем ещё не проданные бумаги | ||
for (var entry : priceService.getRemainingInstruments(user).entrySet()) { | ||
var instrument = investClient.getInstrument(user, entry.getKey()); | ||
var amount = entry.getValue(); | ||
var benefit = benefitByCurrency.getOrDefault(instrument.getCurrency(), BigDecimal.ZERO); | ||
benefit = benefit.add(priceService.getLastPrice(user, instrument.getFigi()).multiply(BigDecimal.valueOf(amount))); | ||
benefitByCurrency.put(instrument.getCurrency(), benefit); | ||
} | ||
var operations = getOperations(user); | ||
// Обрабатываем уже совершённые операции | ||
for (var op : operations) { | ||
if (op.getInstrumentType().equalsIgnoreCase("currency")) continue; | ||
var instrument = getInstrument(user, op.getFigi()); | ||
var benefit = benefitByCurrency.getOrDefault(instrument.getCurrency(), BigDecimal.ZERO); | ||
var payment = Utility.toBigDecimal(op.getPayment()).abs(); | ||
if (op.getOperationType() == OperationType.OPERATION_TYPE_BUY) { | ||
benefit = benefit.subtract(payment); | ||
} else if (op.getOperationType() == OperationType.OPERATION_TYPE_SELL) { | ||
benefit = benefit.add(payment); | ||
} else if (op.getOperationType() == OperationType.OPERATION_TYPE_BROKER_FEE) { | ||
benefit = benefit.subtract(payment); | ||
} else { | ||
log.error("Неизвестная операция {}", op.getOperationType()); | ||
} | ||
benefitByCurrency.put(instrument.getCurrency(), benefit); | ||
} | ||
return new StrategyStatement(benefitByCurrency, operations); | ||
} | ||
StrategyStatement getStatement(User user); | ||
|
||
/** | ||
* Конвертирует заработанные стратегией средства в <code>String</code> | ||
*/ | ||
public String printBenefits(User user) { | ||
var statement = getStatement(user); | ||
var benefitByCurrency = statement.benefitByCurrency(); | ||
if (benefitByCurrency.isEmpty()) { | ||
return "пока ничего :("; | ||
} | ||
return benefitByCurrency.entrySet() | ||
.stream() | ||
.map(entry -> entry.getValue().doubleValue() + " " + entry.getKey().toUpperCase()) | ||
.collect(Collectors.joining(", ")); | ||
} | ||
String printBenefits(User user); | ||
|
||
/** | ||
* Конвертирует <code>OperationType</code> в <code>String</code> | ||
*/ | ||
public String operationTypeToString(OperationType operationType) { | ||
if (operationType == OperationType.OPERATION_TYPE_BUY) { | ||
return "Покупка"; | ||
} | ||
if (operationType == OperationType.OPERATION_TYPE_SELL) { | ||
return "Продажа"; | ||
} | ||
if (operationType == OperationType.OPERATION_TYPE_INPUT) { | ||
return "Пополнение"; | ||
} | ||
if (operationType == OperationType.OPERATION_TYPE_OUTPUT) { | ||
return "Снятие"; | ||
} | ||
if (operationType == OperationType.OPERATION_TYPE_BROKER_FEE) { | ||
return "Комиссия брокера"; | ||
} | ||
return operationType.name(); | ||
} | ||
String operationTypeToString(OperationType operationType); | ||
|
||
/** | ||
* @return возвращает дату операции | ||
*/ | ||
public LocalDateTime getDate(Operation op) { | ||
return LocalDateTime.ofInstant(Utility.toInstant(op.getDate()), ZoneId.systemDefault()); | ||
} | ||
LocalDateTime getDate(Operation op); | ||
|
||
public Map<String, Long> getRemainingInstruments(User user) { | ||
return priceService.getRemainingInstruments(user); | ||
} | ||
/** | ||
* Получение купленных, но ещё не проданных стратегией, бумаг. | ||
*/ | ||
Map<String, Long> getRemainingInstruments(User user); | ||
|
||
public boolean isActive(User user) { | ||
return userService.isActive(user); | ||
} | ||
/** | ||
* Проверка на то, что пользователь активный | ||
*/ | ||
boolean isActive(User user); | ||
|
||
public List<Strategy> getAvailableStrategies() { | ||
return availableStrategies; | ||
} | ||
/** | ||
* Получить доступные для выбора стратегии. | ||
*/ | ||
List<Strategy> getAvailableStrategies(); | ||
|
||
public List<CandleStrategy> getAvailableCandleStrategies() { | ||
return availableCandleStrategies; | ||
} | ||
/** | ||
* Получить доступные для выбора стратегии, использующие свечи. | ||
*/ | ||
List<CandleStrategy> getAvailableCandleStrategies(); | ||
|
||
public List<Operation> getOperations(User user) { | ||
return investClient.getOperations(user); | ||
} | ||
/** | ||
* Получить все операции, произведенных с начала работы приложения по текущий момент. | ||
*/ | ||
List<Operation> getOperations(User user); | ||
|
||
public BigDecimal getBalance(User user) { | ||
return priceService.getBalance(user); | ||
} | ||
/** | ||
* Получить количество потраченных стратегией рублей. | ||
*/ | ||
BigDecimal getBalance(User user); | ||
|
||
public boolean contains(String accountId) { | ||
return userService.contains(accountId); | ||
} | ||
/** | ||
* Проверка на то, что пользователь с данным <code>accountId</code> существует. | ||
*/ | ||
boolean contains(String accountId); | ||
} |
Oops, something went wrong.