Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
binbot-research/
binquant/
*.log
binquant_telegram_user.session

# Editor directories and files
!.vscode/extensions.json
Expand Down
33 changes: 18 additions & 15 deletions api/charts/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,28 +100,19 @@ def migrate_adrs(self) -> int:

return migrated_count

def _get_market_breadth_tickers(self) -> tuple[Iterable[Any], datetime | None]:
if self.exchange == ExchangeId.KUCOIN:
response = self.kucoin_api.spot_api.get_all_tickers()
ticker = response.common_response.data["ticker"]
time = response.common_response.data["time"]
timestamp = datetime.fromtimestamp(time / 1000, tz=timezone.utc)

return ticker or [], timestamp

ticker_data = self.binance_api.ticker_24()
return ticker_data, None

def _normalize_market_breadth_ticker(
self, item: GetSymbolResp, fallback_timestamp: datetime | None = None
) -> dict[str, Any] | None:
if self.exchange == ExchangeId.KUCOIN:
close_time = fallback_timestamp
if item["last"] is None:
# auction coin
return None
return {
"symbol": item["symbol"],
"last_price": float(item.get("last", 0)),
"last_price": float(item["last"]),
"price_change_percent": float(item.get("changeRate", 0)) * 100,
"volume": float(item.get("vol", 0)),
"volume": float(item["vol"]),
"close_time": close_time,
}

Expand Down Expand Up @@ -196,7 +187,19 @@ def ingest_adp_data(self):
and calculate ADR + Strength Index
"""
self._ensure_market_breadth_collection()
market_tickers, fallback_timestamp = self._get_market_breadth_tickers()
if self.exchange == ExchangeId.KUCOIN:
response = self.kucoin_api.spot_api.get_all_tickers()
ticker = response.common_response.data["ticker"]
time = response.common_response.data["time"]
timestamp = datetime.fromtimestamp(time / 1000, tz=timezone.utc)

market_tickers = ticker or []
fallback_timestamp = timestamp
else:
ticker_data = self.binance_api.ticker_24()
market_tickers = ticker_data or []
fallback_timestamp = None

adr_data = self._calculate_adr_series_data(market_tickers, fallback_timestamp)
response = self.kafka_db.market_breadth.insert_one(adr_data.model_dump())
return response
Expand Down
2 changes: 1 addition & 1 deletion api/cronjobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def main():
func=market_domination.ingest_adp_data,
trigger="interval",
timezone=config.timezone,
hours=1,
minutes=30,
id="ingest_adp_data",
)
scheduler.start()
Expand Down
2 changes: 1 addition & 1 deletion api/exchange_apis/kucoin/futures/position_deal.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ def reverse_position(self) -> BotModel:
current_position.mark_price, current_contracts
)

if flip_contracts < current_contracts:
if flip_contracts <= current_contracts:
self.active_bot.add_log(
"Insufficient balance to reverse position after hitting stop loss, closing position with stop loss order."
)
Expand Down
26 changes: 26 additions & 0 deletions api/portfolio/controller.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from datetime import datetime
from math import sqrt
from typing import Union
from account.controller import ConsolidatedAccounts
from databases.crud.autotrade_crud import AutotradeCrud
from databases.crud.balances_crud import BalancesCrud
from pybinbot import (
Expand Down Expand Up @@ -55,6 +56,25 @@ def benchmark_interval(self) -> str:
return KucoinKlineIntervals.ONE_DAY.value
return BinanceKlineIntervals.one_day.value

def _append_live_benchmark_point(
self,
fiat_series: list[float],
btc_series: list[float],
dates: list[int],
balances: list[float],
) -> None:
current_balance = ConsolidatedAccounts(session=self.session).get_balance()
live_net_balance = round_numbers(
current_balance.estimated_total_fiat - current_balance.total_deposit, 4
)
live_btc_price = float(self.api.get_ticker_price(self.benchmark_symbol))
live_timestamp = int(datetime.now().timestamp() * 1000)

fiat_series.append(live_net_balance)
btc_series.append(live_btc_price)
dates.append(live_timestamp)
balances.append(live_net_balance)

def _consolidate_dates(self, klines: list[list], balance_date: int) -> int | None:
balance_date_day = ts_to_day(balance_date)

Expand Down Expand Up @@ -143,6 +163,12 @@ def map_balance_with_benchmark(
btc_series.reverse()
dates.reverse()
balances.reverse()
self._append_live_benchmark_point(
fiat_series=fiat_series,
btc_series=btc_series,
dates=dates,
balances=balances,
)

pnl = 0.0
if len(balances) >= 2:
Expand Down
39 changes: 28 additions & 11 deletions api/tests/test_portfolio.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import datetime, timedelta
from types import SimpleNamespace
from unittest.mock import patch

from fastapi.testclient import TestClient
Expand Down Expand Up @@ -34,21 +35,37 @@ def test_get_benchmark_series(client: TestClient, create_test_tables) -> None:
[balance_points[2][0] - 1, "0", "0", "0", "97000", "0", balance_points[2][0]],
]

with patch(
"portfolio.controller.BinanceApi.get_ui_klines",
return_value=klines,
with (
patch(
"portfolio.controller.BinanceApi.get_ui_klines",
return_value=klines,
),
patch(
"portfolio.controller.BinanceApi.get_ticker_price",
return_value=98000.0,
),
patch(
"portfolio.controller.ConsolidatedAccounts.get_balance",
return_value=SimpleNamespace(
estimated_total_fiat=130.0,
total_deposit=5.0,
),
),
):
response = client.get("/portfolio/benchmark-series")

assert response.status_code == 200

content = response.json()
assert content["message"] == "Successfully retrieved benchmark series."
assert content["data"]["series"] == {
"fiat": [100.0, 110.0, 105.0],
"btc": [95000.0, 96000.0, 97000.0],
"dates": [balance_points[0][0], balance_points[1][0], balance_points[2][0]],
}
assert content["data"]["stats"]["pnl"] == -0.0477
assert content["data"]["stats"]["sharpe"] == 7.1643
assert content["data"]["stats"]["btc_sharpe"] == 3649.0498
assert content["data"]["series"]["fiat"] == [100.0, 110.0, 105.0, 125.0]
assert content["data"]["series"]["btc"] == [95000.0, 96000.0, 97000.0, 98000.0]
assert content["data"]["series"]["dates"][:3] == [
balance_points[0][0],
balance_points[1][0],
balance_points[2][0],
]
assert len(content["data"]["series"]["dates"]) == 4
assert content["data"]["stats"]["pnl"] == 0.16
assert content["data"]["stats"]["sharpe"] == 16.0555
assert content["data"]["stats"]["btc_sharpe"] == 2246.155
17 changes: 13 additions & 4 deletions terminal/src/app/pages/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,28 @@ const usePortfolioPnlDetails = (
): PortfolioPnlDetails => {
const benchmarkSeries =
benchmark?.benchmarkData?.fiat ?? benchmark?.benchmarkData?.fiat;
const previousPortfolioValue = benchmarkSeries?.[benchmarkSeries.length - 1];
const latestPortfolioValue =
accountData?.estimated_total_fiat !== undefined
? accountData.estimated_total_fiat - (accountData?.total_deposit ?? 0)
: undefined;
const lastBenchmarkValue = benchmarkSeries?.[benchmarkSeries.length - 1];
const previousStoredPortfolioValue =
benchmarkSeries && benchmarkSeries.length > 1
? benchmarkSeries[benchmarkSeries.length - 2]
: lastBenchmarkValue;
const previousPortfolioValue =
latestPortfolioValue !== undefined &&
lastBenchmarkValue !== undefined &&
Math.abs(lastBenchmarkValue - latestPortfolioValue) < 0.0001
? previousStoredPortfolioValue
: lastBenchmarkValue;
const portfolioPnlValue =
latestPortfolioValue !== undefined && previousPortfolioValue !== undefined
? latestPortfolioValue - previousPortfolioValue
: undefined;
const portfolioPnlPercentage =
portfolioPnlValue !== undefined && latestPortfolioValue
? (portfolioPnlValue / (latestPortfolioValue - (accountData?.total_deposit ?? 0))) * 100
? (portfolioPnlValue / latestPortfolioValue) * 100
: undefined;
const portfolioPnlClass =
portfolioPnlValue === undefined
Expand Down Expand Up @@ -97,8 +107,7 @@ export const DashboardPage: FC<{}> = () => {
const { portfolioPnlValue, portfolioPnlPercentage, portfolioPnlClass } =
usePortfolioPnlDetails(benchmark, accountData);
const portfolioSharpe = benchmark?.portfolioStats?.sharpe;
const netTotalBalance =
(accountData?.estimated_total_fiat ?? 0) - (accountData?.total_deposit ?? 0);
const netTotalBalance = accountData?.estimated_total_fiat ?? 0;
const btcSharpe = benchmark?.portfolioStats?.btc_sharpe;
const topAlgoCounts = new Set(
algoRanking
Expand Down
2 changes: 1 addition & 1 deletion terminal/src/app/pages/tests/Dashboard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ describe("Dashboard page", () => {
expect(
rtlScreen.getByText("(How efficient are we with risk?)"),
).toBeInTheDocument();
expect(rtlScreen.getByText("105 USDC")).toBeInTheDocument();
expect(rtlScreen.getByText("110 USDC")).toBeInTheDocument();
expect(rtlScreen.getByText("1.27 BTC")).toBeInTheDocument();
});
});
Loading