/* * Copyright (c) 2018, evilny0 * * This file is part of cpfm. * * cpfm 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. * * cpm 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 cpfm. If not, see . * */ #include "exchange.h" #include "exchanges/kraken.h" SQLPP_ALIAS_PROVIDER(bal); ExchangesManager::ExchangesManager(int userId) { m_userId = userId; } ExchangesManager::~ExchangesManager() { } void ExchangesManager::analyzeUserAccounts() { mysql::connection db(getMysqlConfig()); const auto exchanges_accounts = TableExchangesAccounts{}; // Identify coins, we need one analyzer object per exchange. list handlers; for (const auto& row: db.run(select(exchanges_accounts.exchange_id,exchanges_accounts.account_id).from(exchanges_accounts).where(exchanges_accounts.user_id == m_userId))) { int exchangeId = row.exchange_id; int accountId = row.account_id; ExchangeHandler* handler; switch (exchangeId) { case CPFM_EXCHANGE_ID_KRAKEN: handler = new ExchangeHandlerKraken(m_userId,accountId); handlers.push_back(handler); break; default: lerr << "Unsupported exchange: " << exchangeId; break; } } for (auto const& handler: handlers) { handler->analyzeUserData(); delete handler; } } ExchangeHandler::ExchangeHandler(int userId, int accountId) { m_userId = userId; m_accountId = accountId; } ExchangeHandler::~ExchangeHandler() { } void ExchangeHandler::getLedgersTotals() { mysql::connection db(getMysqlConfig()); const auto exchanges_ledgers = TableExchangesLedgers{}; const auto coins = TableCoins{}; map calcBalances; map sqlCalcBalances; map balances; for (const auto& row: db.run(select(all_of(exchanges_ledgers)).from(exchanges_ledgers).unconditionally())) { Money amount(row.amount); Money fee(row.fee); Money x = calcBalances[row.coin_id]; Money prevBalance(x); x = x + amount - fee; calcBalances[row.coin_id] = x; balances[row.coin_id] = x; } for (const auto& row: db.run(select(exchanges_ledgers.coin_id,sum(exchanges_ledgers.amount-exchanges_ledgers.fee).as(bal)).from(exchanges_ledgers).unconditionally().group_by(exchanges_ledgers.coin_id))) { Money x(row.bal); sqlCalcBalances[row.coin_id] = x; } } void ExchangeHandler::getTradesTotals() { mysql::connection db(getMysqlConfig()); const auto exchanges_trades = TableExchangesTrades{}; const auto coins = TableCoins{}; map medianBuyPrices; map balances; map earnings; map coinNames; for (const auto& row: db.run(select(all_of(coins)).from(coins).unconditionally())) { coinNames[row.coin_id] = row.coin_short; } for (const auto& row: db.run(select(all_of(exchanges_trades)).from(exchanges_trades).unconditionally())) { Money price(row.price); Money cost(row.cost); Money fee(row.fee); Money volume(row.volume); int baseCoinId = row.base_coin_id; int quoteCoinId = row.quote_coin_id; int tradeType = row.type; std::string sType; Money prevMedianPrice(medianBuyPrices[baseCoinId]); Money prevBalance(balances[baseCoinId]); if (tradeType == CPFM_TRADE_TYPE_BUY) { sType = "buy"; medianBuyPrices[baseCoinId] = (medianBuyPrices[baseCoinId]*balances[baseCoinId] + price*volume) / (balances[baseCoinId]+volume); balances[baseCoinId] += volume; } else if (tradeType == CPFM_TRADE_TYPE_SELL) { sType = "sale"; // median price does not change on sale, except if we reach balance 0. It should adjust correctly on next buy. if (balances[baseCoinId] < volume) { lerr << "Trying to sell " << volume << " but balance is " << balances[baseCoinId]; } else { balances[baseCoinId] -= volume; earnings[baseCoinId] += (price-medianBuyPrices[baseCoinId])*volume; } } } } void ExchangeHandler::emptyLedgers() { mysql::connection db(getMysqlConfig()); const auto exchanges_ledgers = TableExchangesLedgers{}; db.run(remove_from(exchanges_ledgers).unconditionally()); linfo << "Emptied Ledgers DB table"; } void ExchangeHandler::emptyTrades() { mysql::connection db(getMysqlConfig()); const auto exchanges_trades = TableExchangesTrades{}; db.run(remove_from(exchanges_trades).unconditionally()); linfo << "Emptied Trades DB table"; }