182 lines
5.2 KiB
C++
182 lines
5.2 KiB
C++
|
/*
|
||
|
* 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 <http://www.gnu.org/licenses/>.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#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<ExchangeHandler*> 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<int,Money> calcBalances;
|
||
|
map<int,Money> sqlCalcBalances;
|
||
|
map<int,Money> 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<int,Money> medianBuyPrices;
|
||
|
map<int,Money> balances;
|
||
|
map<int,Money> earnings;
|
||
|
|
||
|
map<int,std::string> 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";
|
||
|
}
|