Search in sources :

Example 6 with AccountDTO

use of tech.cassandre.trading.bot.dto.user.AccountDTO in project cassandre-trading-bot by cassandre-tech.

the class AccountDTOTest method checkEqualToForBalancesList.

@Test
@DisplayName("Check equals() on balances list")
public void checkEqualToForBalancesList() {
    Set<BalanceDTO> balances = new LinkedHashSet<>();
    // Account 1 - No balances.
    AccountDTO account1 = AccountDTO.builder().balances(balances).build();
    // Account 2 - One more balance in account 1.
    balances.add(BalanceDTO.builder().build());
    AccountDTO account2 = AccountDTO.builder().balances(balances).build();
    balances.clear();
    assertNotEquals(account1, account2);
    assertNotEquals(account2, account1);
    // Account 3 - One ETH & one BTC.
    balances.add(BalanceDTO.builder().currency(ETH).build());
    balances.add(BalanceDTO.builder().currency(BTC).build());
    AccountDTO account3 = AccountDTO.builder().balances(balances).build();
    balances.clear();
    // Account 4 - One BTC & one ETH (inverted compared to account 3).
    balances.add(BalanceDTO.builder().currency(BTC).build());
    balances.add(BalanceDTO.builder().currency(ETH).build());
    AccountDTO account4 = AccountDTO.builder().balances(balances).build();
    balances.clear();
    assertEquals(account3, account4);
    assertEquals(account4, account3);
    // Account 5 - One BTC & one USDT (inverted).
    balances.add(BalanceDTO.builder().currency(BTC).build());
    balances.add(BalanceDTO.builder().currency(USDT).build());
    AccountDTO account5 = AccountDTO.builder().balances(balances).build();
    balances.clear();
    assertNotEquals(account4, account5);
    assertNotEquals(account5, account4);
}
Also used : LinkedHashSet(java.util.LinkedHashSet) BalanceDTO(tech.cassandre.trading.bot.dto.user.BalanceDTO) AccountDTO(tech.cassandre.trading.bot.dto.user.AccountDTO) Test(org.junit.jupiter.api.Test) DisplayName(org.junit.jupiter.api.DisplayName)

Example 7 with AccountDTO

use of tech.cassandre.trading.bot.dto.user.AccountDTO in project cassandre-trading-bot by cassandre-tech.

the class TradeServiceDryModeAOP method createBuyMarketOrder.

@Around(value = "execution(* tech.cassandre.trading.bot.service.TradeService.createBuyMarketOrder(..)) && args(strategy, currencyPair, amount)", argNames = "pjp, strategy, currencyPair, amount")
public final OrderCreationResultDTO createBuyMarketOrder(final ProceedingJoinPoint pjp, final CassandreStrategy strategy, final CurrencyPairDTO currencyPair, final BigDecimal amount) {
    // We check that we have the trade account.
    final Optional<AccountDTO> tradeAccount = strategy.getTradeAccount();
    if (tradeAccount.isEmpty()) {
        throw new DryModeException("Trade account was not found");
    }
    // We check if we have enough assets to buy.
    // Buying order - we buy ETH with BTC.
    // We are buying the following amount: ticker last price * amount
    Optional<BalanceDTO> balance = tradeAccount.get().getBalance(currencyPair.getQuoteCurrency());
    final Optional<TickerDTO> ticker = strategy.getLastTickerByCurrencyPair(currencyPair);
    if (balance.isPresent() && ticker.isPresent()) {
        BigDecimal ownedAssets = balance.get().getAvailable();
        BigDecimal cost = ticker.get().getLast().multiply(amount);
        if (cost.compareTo(ownedAssets) > 0) {
            final String errorMessage = "Not enough assets (costs: " + cost + " " + currencyPair.getQuoteCurrency() + " - owned assets: " + ownedAssets + " " + currencyPair.getQuoteCurrency() + ")";
            return new OrderCreationResultDTO(errorMessage, new RuntimeException());
        }
    } else {
        return new OrderCreationResultDTO("No assets (" + currencyPair.getQuoteCurrency() + ")", new RuntimeException());
    }
    // We execute the buy.
    Object result = null;
    try {
        result = pjp.proceed();
    } catch (Throwable throwable) {
        logger.error("Error in Dry mode AOP: {}", throwable.getMessage());
    }
    // We update the account.
    userService.addToBalance(strategy, CURRENCY_MAPPER.mapToCurrency(currencyPair.getBaseCurrency()), amount);
    userService.addToBalance(strategy, CURRENCY_MAPPER.mapToCurrency(currencyPair.getQuoteCurrency()), amount.multiply(ticker.get().getLast()).multiply(new BigDecimal("-1")));
    return (OrderCreationResultDTO) result;
}
Also used : TickerDTO(tech.cassandre.trading.bot.dto.market.TickerDTO) DryModeException(tech.cassandre.trading.bot.util.exception.DryModeException) OrderCreationResultDTO(tech.cassandre.trading.bot.dto.trade.OrderCreationResultDTO) BalanceDTO(tech.cassandre.trading.bot.dto.user.BalanceDTO) BigDecimal(java.math.BigDecimal) AccountDTO(tech.cassandre.trading.bot.dto.user.AccountDTO) Around(org.aspectj.lang.annotation.Around)

Example 8 with AccountDTO

use of tech.cassandre.trading.bot.dto.user.AccountDTO in project cassandre-trading-bot by cassandre-tech.

the class UserServiceDryModeAOP method addToBalance.

/**
 * Update balance of trade account (method called by trade service).
 *
 * @param strategy strategy
 * @param currency currency
 * @param amount   amount
 */
public void addToBalance(final CassandreStrategy strategy, final Currency currency, final BigDecimal amount) {
    final Optional<AccountDTO> tradeAccount = strategy.getTradeAccount();
    if (tradeAccount.isEmpty()) {
        logger.error("Trading account not found!");
    } else {
        // We build a new account information from what we saved.
        Collection<Wallet> wallets = new LinkedHashSet<>();
        // We retreat all the wallets we have.
        accountInfo.getWallets().forEach((name, wallet) -> {
            HashMap<Currency, Balance> balances = new LinkedHashMap<>();
            // For each balance, we add it if nothing changed or, if on trading account, and we need to change the amount,
            // Then we do it.
            wallet.getBalances().forEach((balanceCurrency, balance) -> {
                if (name.equals(tradeAccount.get().getName()) && balanceCurrency.equals(currency)) {
                    // If we are on the account and currency to update, we calculate the new value.
                    balances.put(balanceCurrency, new Balance(balanceCurrency, balance.getTotal().add(amount)));
                } else {
                    // Else we keep the same value.
                    balances.put(balanceCurrency, balance);
                }
            });
            // amounts, then we create a new balance.
            if (name.equals(tradeAccount.get().getName()) && balances.get(currency) == null) {
                balances.put(currency, new Balance(currency, amount));
            }
            // We add the wallet.
            wallets.add(new Wallet(name, name, balances.values(), Collections.emptySet(), ZERO, ZERO));
        });
        // Creates the account info.
        accountInfo = new AccountInfo(USER_ID, wallets);
        // Updates all strategies.
        final UserDTO userDTO = ACCOUNT_MAPPER.mapToUserDTO(accountInfo);
        applicationContext.getBeansWithAnnotation(tech.cassandre.trading.bot.strategy.CassandreStrategy.class).values().stream().map(o -> (CassandreStrategyInterface) o).forEach(cassandreStrategyInterface -> cassandreStrategyInterface.initializeAccounts(userDTO.getAccounts()));
    }
}
Also used : LinkedHashSet(java.util.LinkedHashSet) Arrays(java.util.Arrays) Scanner(java.util.Scanner) HashMap(java.util.HashMap) Balance(org.knowm.xchange.dto.account.Balance) PathMatchingResourcePatternResolver(org.springframework.core.io.support.PathMatchingResourcePatternResolver) LinkedHashMap(java.util.LinkedHashMap) CassandreStrategyInterface(tech.cassandre.trading.bot.strategy.internal.CassandreStrategyInterface) BigDecimal(java.math.BigDecimal) ConditionalOnExpression(org.springframework.boot.autoconfigure.condition.ConditionalOnExpression) Aspect(org.aspectj.lang.annotation.Aspect) LinkedHashSet(java.util.LinkedHashSet) Resource(org.springframework.core.io.Resource) AccountInfo(org.knowm.xchange.dto.account.AccountInfo) Wallet(org.knowm.xchange.dto.account.Wallet) AccountDTO(tech.cassandre.trading.bot.dto.user.AccountDTO) CassandreStrategy(tech.cassandre.trading.bot.strategy.internal.CassandreStrategy) Collection(java.util.Collection) ZERO(java.math.BigDecimal.ZERO) IOException(java.io.IOException) ApplicationContext(org.springframework.context.ApplicationContext) Around(org.aspectj.lang.annotation.Around) FileNotFoundException(java.io.FileNotFoundException) UserDTO(tech.cassandre.trading.bot.dto.user.UserDTO) Currency(org.knowm.xchange.currency.Currency) Component(org.springframework.stereotype.Component) List(java.util.List) BaseService(tech.cassandre.trading.bot.util.base.service.BaseService) Optional(java.util.Optional) Collections(java.util.Collections) ProceedingJoinPoint(org.aspectj.lang.ProceedingJoinPoint) Wallet(org.knowm.xchange.dto.account.Wallet) UserDTO(tech.cassandre.trading.bot.dto.user.UserDTO) CassandreStrategyInterface(tech.cassandre.trading.bot.strategy.internal.CassandreStrategyInterface) LinkedHashMap(java.util.LinkedHashMap) Currency(org.knowm.xchange.currency.Currency) AccountDTO(tech.cassandre.trading.bot.dto.user.AccountDTO) Balance(org.knowm.xchange.dto.account.Balance) AccountInfo(org.knowm.xchange.dto.account.AccountInfo)

Example 9 with AccountDTO

use of tech.cassandre.trading.bot.dto.user.AccountDTO in project cassandre-trading-bot by cassandre-tech.

the class BasicCassandreStrategyTest method checkStrategyBehavior.

@Test
@DisplayName("Check strategy behavior")
public void checkStrategyBehavior() {
    // =============================================================================================================
    // We check that the strategy is correctly registered in database.
    Optional<Strategy> strategyInDatabase = strategyRepository.findByStrategyId("01");
    assertTrue(strategyInDatabase.isPresent());
    assertEquals(1, strategyInDatabase.get().getUid());
    assertEquals("01", strategyInDatabase.get().getStrategyId());
    assertEquals("Testable strategy", strategyInDatabase.get().getName());
    // =============================================================================================================
    // We check accounts updates (4 replies : account 01, account 02, account 03 and again account 03).
    // But the 4 replies only concerns 3 account.
    // We have 1 BTC, 10 ETH and 100 USDT.
    with().await().untilAsserted(() -> assertEquals(4, strategy.getAccountsUpdatesReceived().size()));
    assertEquals(3, strategy.getAccounts().size());
    // Testing trade account.
    final Optional<AccountDTO> tradeAccount = strategy.getTradeAccount();
    assertTrue(tradeAccount.isPresent());
    assertEquals("03", tradeAccount.get().getAccountId());
    assertEquals("trade", tradeAccount.get().getName());
    // Testing trade account balances.
    assertEquals(3, strategy.getTradeAccountBalances().size());
    assertEquals(0, new BigDecimal("1").compareTo(strategy.getTradeAccountBalances().get(BTC).getAvailable()));
    assertEquals(0, new BigDecimal("10").compareTo(strategy.getTradeAccountBalances().get(ETH).getAvailable()));
    assertEquals(0, new BigDecimal("100").compareTo(strategy.getTradeAccountBalances().get(USDT).getAvailable()));
    // =============================================================================================================
    // We check that all tickers arrived (6 ETH/BTC & 1 ETH/USDT).
    // 1 ETH = 6 BTC.
    // 1 ETH = 10 000 USDT.
    with().await().untilAsserted(() -> assertEquals(7, strategy.getTickersUpdatesReceived().size()));
    assertEquals(2, strategy.getLastTickers().size());
    assertNotNull(strategy.getLastTickers().get(ETH_BTC));
    assertNotNull(strategy.getLastTickers().get(ETH_USDT));
    assertEquals(0, new BigDecimal("6").compareTo(strategy.getLastTickers().get(ETH_BTC).getLast()));
    assertEquals(0, new BigDecimal("10000").compareTo(strategy.getLastTickers().get(ETH_USDT).getLast()));
    // =============================================================================================================
    // Check getEstimatedBuyingCost().
    // As 1 ETH cost 6 BTC, 2 ETH would cost 12 BTC.
    final Optional<CurrencyAmountDTO> estimatedBuyingCost = strategy.getEstimatedBuyingCost(ETH_BTC, new BigDecimal(2));
    assertTrue(estimatedBuyingCost.isPresent());
    assertEquals(0, new BigDecimal("12").compareTo(estimatedBuyingCost.get().getValue()));
    // =============================================================================================================
    // Check getEstimatedBuyableAmount()
    // As 1 ETH cost 10 000 USDT, with 5 000 USDT, I should be able to buy 0.5 ETH.
    // And I check I can't 260 EUR with ETH as I don't have EURO price.
    // And I check I can't buy USDT with EURO as I don't have EURO.
    final Optional<BigDecimal> estimatedBuyableAmount = strategy.getEstimatedBuyableAmount(new CurrencyAmountDTO(new BigDecimal(5000), USDT), ETH);
    assertTrue(estimatedBuyableAmount.isPresent());
    assertEquals(0, new BigDecimal("0.5").compareTo(estimatedBuyableAmount.get()));
    assertFalse(strategy.getEstimatedBuyableAmount(new CurrencyAmountDTO(new BigDecimal(260), EUR), ETH).isPresent());
    assertFalse(strategy.getEstimatedBuyableAmount(new CurrencyAmountDTO(new BigDecimal(260), USDT), EUR).isPresent());
    // =============================================================================================================
    // Check canBuy() & canSell().
    // Our assets: we have 1 BTC, 10 ETH and 100 USDT.
    // 1 ETH costs 6 BTC.
    // 1 ETH costs 10 000 USDT.
    final AccountDTO account = strategy.getAccounts().get("03");
    assertNotNull(account);
    // canBuy().
    // Buying something for an asset we don't have.
    assertFalse(strategy.canBuy(BTC_ETH, new BigDecimal("0.00001")));
    assertFalse(strategy.canBuy(account, BTC_ETH, new BigDecimal("0.00001")));
    // Trying to buy a full bitcoin, but we only have 2 000 USDT.
    assertFalse(strategy.canBuy(ETH_USDT, new BigDecimal("1")));
    assertFalse(strategy.canBuy(account, ETH_USDT, new BigDecimal("1")));
    // Trying to buy a 0.01 ETH that will cost 100 USDT, and we have 100 USDT, it should work but not for 0.011.
    assertTrue(strategy.canBuy(ETH_USDT, new BigDecimal("0.01")));
    assertTrue(strategy.canBuy(account, ETH_USDT, new BigDecimal("0.01")));
    assertFalse(strategy.canBuy(account, ETH_USDT, new BigDecimal("0.011")));
    // Trying to buy a 0.01 bitcoin that costs 100 USDT (we have 100 USDT). But we want to have 1 USDT left.
    assertFalse(strategy.canBuy(ETH_USDT, new BigDecimal("0.01"), new BigDecimal("1")));
    assertFalse(strategy.canBuy(ETH_USDT, new BigDecimal("0.01"), new BigDecimal("1")));
    assertFalse(strategy.canBuy(account, ETH_USDT, new BigDecimal("0.01"), new BigDecimal("1")));
    assertFalse(strategy.canBuy(account, ETH_USDT, new BigDecimal("0.01"), new BigDecimal("1")));
    // canSell().
    // Selling an asset we don't have (EUR).
    assertFalse(strategy.canSell(EUR, new BigDecimal("0.00001")));
    assertFalse(strategy.canSell(account, EUR, new BigDecimal("0.00001")));
    // Trying to sell 1 BTC (we have them).
    assertTrue(strategy.canSell(BTC, new BigDecimal("1")));
    assertTrue(strategy.canSell(account, BTC, new BigDecimal("1")));
    // Trying to sell 3 BTC (we don't have them).
    assertFalse(strategy.canSell(BTC, new BigDecimal("3")));
    assertFalse(strategy.canSell(account, BTC, new BigDecimal("3")));
    // Trying to sell 1 BTC and still have 1 (not possible).
    assertFalse(strategy.canSell(BTC, new BigDecimal("1"), new BigDecimal("1")));
    assertFalse(strategy.canSell(account, BTC, new BigDecimal("1"), new BigDecimal("1")));
}
Also used : CurrencyAmountDTO(tech.cassandre.trading.bot.dto.util.CurrencyAmountDTO) Strategy(tech.cassandre.trading.bot.domain.Strategy) TestableCassandreStrategy(tech.cassandre.trading.bot.test.util.strategies.TestableCassandreStrategy) AccountDTO(tech.cassandre.trading.bot.dto.user.AccountDTO) BigDecimal(java.math.BigDecimal) BaseTest(tech.cassandre.trading.bot.test.util.junit.BaseTest) Test(org.junit.jupiter.api.Test) SpringBootTest(org.springframework.boot.test.context.SpringBootTest) DisplayName(org.junit.jupiter.api.DisplayName)

Example 10 with AccountDTO

use of tech.cassandre.trading.bot.dto.user.AccountDTO in project cassandre-trading-bot by cassandre-tech.

the class BasicCassandreStrategyTestMock method userService.

@SuppressWarnings("unchecked")
@Bean
@Primary
public UserService userService() {
    Set<BalanceDTO> balances = new LinkedHashSet<>();
    final Map<String, AccountDTO> accounts = new LinkedHashMap<>();
    UserService userService = mock(UserService.class);
    // =============================================================================================================
    // Account retrieved by configuration - Empty, just allowing the getTradeAccount() in configuration to work.
    AccountDTO tempAccount = AccountDTO.builder().accountId("03").name("trade").build();
    accounts.put("03", tempAccount);
    UserDTO tempUser = UserDTO.builder().accounts(accounts).build();
    accounts.clear();
    // =============================================================================================================
    // Creating the response to the first call of the AccountFlux.
    // User has three accounts:
    // - Account 01 (No name with 1 BTC).
    // - Account 02 (No name with 1 ETH).
    // - Account 03 (Trade account with 2 BTC, 10 ETH, 2 000 USDT).
    // User service response 01.
    BalanceDTO account01Balance1 = BalanceDTO.builder().currency(BTC).available(new BigDecimal("1")).build();
    balances.add(account01Balance1);
    AccountDTO account01 = AccountDTO.builder().accountId("01").balances(balances).build();
    accounts.put("01", account01);
    UserDTO userResponse01 = UserDTO.builder().accounts(accounts).build();
    balances.clear();
    accounts.clear();
    // User service response 02.
    BalanceDTO account02Balance1 = BalanceDTO.builder().currency(ETH).available(new BigDecimal("1")).build();
    balances.add(account02Balance1);
    AccountDTO account02 = AccountDTO.builder().accountId("02").balances(balances).build();
    accounts.put("02", account02);
    UserDTO userResponse02 = UserDTO.builder().accounts(accounts).build();
    balances.clear();
    accounts.clear();
    // User service response 03.
    balances.add(BalanceDTO.builder().currency(BTC).available(new BigDecimal("2")).build());
    balances.add(BalanceDTO.builder().currency(ETH).available(new BigDecimal("10")).build());
    balances.add(BalanceDTO.builder().currency(EUR).available(new BigDecimal("2000")).build());
    AccountDTO account03 = AccountDTO.builder().accountId("03").name("trade").balances(balances).build();
    accounts.put("03", account03);
    UserDTO userResponse03 = UserDTO.builder().accounts(accounts).build();
    balances.clear();
    accounts.clear();
    // User service response 04.
    balances.add(BalanceDTO.builder().currency(BTC).available(new BigDecimal("1")).build());
    balances.add(BalanceDTO.builder().currency(ETH).available(new BigDecimal("10")).build());
    balances.add(BalanceDTO.builder().currency(USDT).available(new BigDecimal("100")).build());
    AccountDTO account04 = AccountDTO.builder().accountId("03").name("trade").balances(balances).build();
    accounts.put("03", account04);
    UserDTO userResponse04 = UserDTO.builder().accounts(accounts).build();
    balances.clear();
    accounts.clear();
    // We have two different mock replies.
    // StrategiesAutoConfiguration calls userService.getUser().
    given(userService.getUser()).willReturn(Optional.of(tempUser));
    // AccountFlux calls userService.getAccounts()
    given(userService.getAccounts()).willReturn(userResponse01.getAccounts(), userResponse02.getAccounts(), userResponse03.getAccounts(), userResponse04.getAccounts());
    return userService;
}
Also used : LinkedHashSet(java.util.LinkedHashSet) UserService(tech.cassandre.trading.bot.service.UserService) UserDTO(tech.cassandre.trading.bot.dto.user.UserDTO) BalanceDTO(tech.cassandre.trading.bot.dto.user.BalanceDTO) AccountDTO(tech.cassandre.trading.bot.dto.user.AccountDTO) BigDecimal(java.math.BigDecimal) LinkedHashMap(java.util.LinkedHashMap) Primary(org.springframework.context.annotation.Primary) Bean(org.springframework.context.annotation.Bean)

Aggregations

AccountDTO (tech.cassandre.trading.bot.dto.user.AccountDTO)23 BigDecimal (java.math.BigDecimal)18 DisplayName (org.junit.jupiter.api.DisplayName)14 Test (org.junit.jupiter.api.Test)14 BalanceDTO (tech.cassandre.trading.bot.dto.user.BalanceDTO)12 UserDTO (tech.cassandre.trading.bot.dto.user.UserDTO)11 SpringBootTest (org.springframework.boot.test.context.SpringBootTest)10 BaseTest (tech.cassandre.trading.bot.test.util.junit.BaseTest)7 LinkedHashSet (java.util.LinkedHashSet)6 Around (org.aspectj.lang.annotation.Around)6 TickerDTO (tech.cassandre.trading.bot.dto.market.TickerDTO)5 LinkedHashMap (java.util.LinkedHashMap)4 OrderCreationResultDTO (tech.cassandre.trading.bot.dto.trade.OrderCreationResultDTO)4 IOException (java.io.IOException)3 Collections (java.util.Collections)3 Optional (java.util.Optional)3 Tag (org.junit.jupiter.api.Tag)3 Strategy (tech.cassandre.trading.bot.domain.Strategy)3 DryModeException (tech.cassandre.trading.bot.util.exception.DryModeException)3 FileNotFoundException (java.io.FileNotFoundException)2