From ac2b60d3af4fbefe38eb5f5627b5a305e7856171 Mon Sep 17 00:00:00 2001 From: aftermath2 Date: Sat, 1 Apr 2023 12:00:00 +0000 Subject: [PATCH 1/3] Replace API values with frontend orders aggregation --- api/serializers.py | 10 ----- api/tests/test_utils.py | 38 ------------------- api/utils.py | 27 ------------- api/views.py | 20 +--------- .../TradeBox/Prompts/PublicWait.tsx | 35 +++++++++++++++-- frontend/src/models/Order.model.ts | 2 - 6 files changed, 34 insertions(+), 98 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index dc10ac25..d51fd5bd 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -237,14 +237,6 @@ class OrderDetailSerializer(serializers.ModelSerializer): premium_now = serializers.FloatField( required=False, help_text="Premium over the CEX price at the current time" ) - premium_percentile = serializers.FloatField( - required=False, - help_text="(Only if `is_maker`) Premium percentile of your order compared to other public orders in the same currency currently in the order book", - ) - num_similar_orders = serializers.IntegerField( - required=False, - help_text="(Only if `is_maker`) The number of public orders of the same currency currently in the order book", - ) tg_enabled = serializers.BooleanField( required=False, help_text="(Only if `is_maker`) Whether Telegram notification is enabled or not", @@ -446,8 +438,6 @@ class OrderDetailSerializer(serializers.ModelSerializer): "maker_status", "taker_status", "price_now", - "premium_percentile", - "num_similar_orders", "tg_enabled", "tg_token", "tg_bot_name", diff --git a/api/tests/test_utils.py b/api/tests/test_utils.py index 0cf427ca..306989db 100644 --- a/api/tests/test_utils.py +++ b/api/tests/test_utils.py @@ -8,7 +8,6 @@ from api.models import Order from api.utils import ( base91_to_hex, bitcoind_rpc, - compute_premium_percentile, get_cln_version, get_exchange_rates, get_lnd_version, @@ -123,43 +122,6 @@ class TestUtils(TestCase): # Assert that the read method of the file object was called mock_file().read.assert_called_once() - @patch("api.utils.Order.objects.filter") - def test_compute_premium_percentile(self, mock_filter): - # Mock the filter method to return a mock queryset - mock_queryset = MagicMock() - mock_filter.return_value = mock_queryset - - # Mock the exclude method of the queryset to return the same mock queryset - mock_queryset.exclude.return_value = mock_queryset - - # Mock the count method of the queryset to return a specific number - mock_queryset.count.return_value = 2 - - # Mock the order object - order = MagicMock() - order.currency = "USD" - order.status = Order.Status.PUB - order.type = "type" - order.id = 1 - order.amount = 1000 - order.has_range = False - order.max_amount = 2000 - order.last_satoshis = 10000 - - # Call the compute_premium_percentile function with the mock order object - percentile = compute_premium_percentile(order) - - # Assert that the function returns a float - self.assertIsInstance(percentile, float) - - # Assert that the filter method of the queryset was called with the correct arguments - mock_filter.assert_called_once_with( - currency=order.currency, status=Order.Status.PUB, type=order.type - ) - - # Assert that the exclude method of the queryset was called with the correct arguments - mock_queryset.exclude.assert_called_once_with(id=order.id) - def test_weighted_median(self): values = [1, 2, 3, 4, 5] weights = [1, 1, 1, 1, 1] diff --git a/api/utils.py b/api/utils.py index e74107e1..f95907dc 100644 --- a/api/utils.py +++ b/api/utils.py @@ -303,33 +303,6 @@ def get_robosats_commit(): return commit_hash -premium_percentile = {} - - -@ring.dict(premium_percentile, expire=300) -def compute_premium_percentile(order): - queryset = Order.objects.filter( - currency=order.currency, status=Order.Status.PUB, type=order.type - ).exclude(id=order.id) - - if len(queryset) <= 1: - return 0.5 - - amount = order.amount if not order.has_range else order.max_amount - order_rate = float(order.last_satoshis) / float(amount) - rates = [] - for similar_order in queryset: - similar_order_amount = ( - similar_order.amount - if not similar_order.has_range - else similar_order.max_amount - ) - rates.append(float(similar_order.last_satoshis) / float(similar_order_amount)) - - rates = np.array(rates) - return round(np.sum(rates < order_rate) / len(rates), 2) - - def weighted_median(values, sample_weight=None, quantiles=0.5, values_sorted=False): """Very close to numpy.percentile, but it supports weights. NOTE: quantiles should be in [0, 1]! diff --git a/api/views.py b/api/views.py index c2ac4541..cb9d48ec 100644 --- a/api/views.py +++ b/api/views.py @@ -58,7 +58,6 @@ from api.serializers import ( ) from api.utils import ( compute_avg_premium, - compute_premium_percentile, get_cln_version, get_lnd_version, get_robosats_commit, @@ -294,19 +293,6 @@ class OrderView(viewsets.ViewSet): else: data["satoshis_now"] = Logics.satoshis_now(order) - # 4. a) If maker and Public/Paused, add premium percentile - # num similar orders, and maker information to enable telegram notifications. - if data["is_maker"] and order.status in [ - Order.Status.PUB, - Order.Status.PAU, - ]: - data["premium_percentile"] = compute_premium_percentile(order) - data["num_similar_orders"] = len( - Order.objects.filter( - currency=order.currency, status=Order.Status.PUB - ) - ) - # For participants add positions, nicks and status as a message and hold invoices status data["is_buyer"] = Logics.is_buyer(order, request.user) data["is_seller"] = Logics.is_seller(order, request.user) @@ -602,7 +588,6 @@ class OrderView(viewsets.ViewSet): # 3) If action is 'update invoice' elif action == "update_invoice": - # DEPRECATE post v0.5.1. valid_signature, invoice = verify_signed_message( request.user.robot.public_key, pgp_invoice ) @@ -660,7 +645,7 @@ class OrderView(viewsets.ViewSet): if not valid: return Response(context, status.HTTP_400_BAD_REQUEST) - # 7) If action is rate + # 7) If action is rate_user elif action == "rate_user" and rating: """No user rating""" pass @@ -671,7 +656,7 @@ class OrderView(viewsets.ViewSet): if not valid: return Response(context, status.HTTP_400_BAD_REQUEST) - # 9) If action is rate_platform + # 9) If action is pause elif action == "pause": valid, context = Logics.pause_unpause_public_order(order, request.user) if not valid: @@ -867,7 +852,6 @@ class InfoView(viewsets.ViewSet): context["node_id"] = config("NODE_ID") context["network"] = config("NETWORK", cast=str, default="mainnet") context["maker_fee"] = float(config("FEE")) * float(config("MAKER_FEE_SPLIT")) - context["maker_fee"] = float(config("FEE")) * float(config("MAKER_FEE_SPLIT")) context["taker_fee"] = float(config("FEE")) * ( 1 - float(config("MAKER_FEE_SPLIT")) ) diff --git a/frontend/src/components/TradeBox/Prompts/PublicWait.tsx b/frontend/src/components/TradeBox/Prompts/PublicWait.tsx index 79d942f7..8ba488e5 100644 --- a/frontend/src/components/TradeBox/Prompts/PublicWait.tsx +++ b/frontend/src/components/TradeBox/Prompts/PublicWait.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useContext, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { List, @@ -15,6 +15,7 @@ import { LoadingButton } from '@mui/lab'; import currencies from '../../../../static/assets/currencies.json'; import { type Order } from '../../../models'; +import { FederationContext, type UseFederationStoreType } from '../../../contexts/FederationContext'; import { PauseCircle, Storefront, Percent } from '@mui/icons-material'; interface PublicWaitPrompProps { @@ -29,6 +30,8 @@ export const PublicWaitPrompt = ({ onClickPauseOrder, }: PublicWaitPrompProps): React.JSX.Element => { const { t } = useTranslation(); + const { federation } = useContext(FederationContext); + const currencyCode = currencies[order.currency.toString()]; const depositHoursMinutes = function (): { @@ -41,6 +44,32 @@ export const PublicWaitPrompt = ({ return dict; }; + const numSimilarOrders = useMemo((): number => { + const orders = Object.values(federation.book) ?? []; + const similarOrders = orders.filter(bookOrder => + // Public -> 1 + bookOrder?.currency == order.currency && order.status == 1 + ) + return similarOrders.length + }, [federation.book]); + + const premiumPercentile = useMemo((): number => { + const orders = Object.values(federation.book) ?? []; + const querySet = orders.filter(bookOrder => + bookOrder?.currency == order.currency && order.status == 1 && bookOrder.type == order.type + ) + + if (querySet.length <= 1) { + return 0.5 + } + + const premiums = querySet.map(similarOrder => parseFloat(similarOrder?.premium ?? '0')) + const sumPremium = premiums.reduce((sum, rate) => sum + (rate < order.premium ? 1 : 0), 0) + const percentile = (sumPremium / premiums.length) * 100 + + return Math.floor(parseFloat(percentile.toFixed(2))); + }, [federation.book]); + return ( @@ -69,7 +98,7 @@ export const PublicWaitPrompt = ({ Date: Sat, 1 Apr 2023 12:00:00 +0000 Subject: [PATCH 2/3] Add missing semi-colons --- .../src/components/TradeBox/Prompts/PublicWait.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/TradeBox/Prompts/PublicWait.tsx b/frontend/src/components/TradeBox/Prompts/PublicWait.tsx index 8ba488e5..81076ffc 100644 --- a/frontend/src/components/TradeBox/Prompts/PublicWait.tsx +++ b/frontend/src/components/TradeBox/Prompts/PublicWait.tsx @@ -49,23 +49,23 @@ export const PublicWaitPrompt = ({ const similarOrders = orders.filter(bookOrder => // Public -> 1 bookOrder?.currency == order.currency && order.status == 1 - ) - return similarOrders.length + ); + return similarOrders.length; }, [federation.book]); const premiumPercentile = useMemo((): number => { const orders = Object.values(federation.book) ?? []; const querySet = orders.filter(bookOrder => bookOrder?.currency == order.currency && order.status == 1 && bookOrder.type == order.type - ) + ); if (querySet.length <= 1) { - return 0.5 + return 0.5; } - const premiums = querySet.map(similarOrder => parseFloat(similarOrder?.premium ?? '0')) - const sumPremium = premiums.reduce((sum, rate) => sum + (rate < order.premium ? 1 : 0), 0) - const percentile = (sumPremium / premiums.length) * 100 + const premiums = querySet.map(similarOrder => parseFloat(similarOrder?.premium ?? '0')); + const sumPremium = premiums.reduce((sum, rate) => sum + (rate < order.premium ? 1 : 0), 0); + const percentile = (sumPremium / premiums.length) * 100; return Math.floor(parseFloat(percentile.toFixed(2))); }, [federation.book]); From 833fe2bc29c1af4a647ed9a1b5b6ee2526d5a139 Mon Sep 17 00:00:00 2001 From: aftermath2 Date: Sat, 1 Apr 2023 12:00:00 +0000 Subject: [PATCH 3/3] Use federation store updated at field --- frontend/src/components/TradeBox/Prompts/PublicWait.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/TradeBox/Prompts/PublicWait.tsx b/frontend/src/components/TradeBox/Prompts/PublicWait.tsx index 81076ffc..ae24ad2f 100644 --- a/frontend/src/components/TradeBox/Prompts/PublicWait.tsx +++ b/frontend/src/components/TradeBox/Prompts/PublicWait.tsx @@ -30,7 +30,7 @@ export const PublicWaitPrompt = ({ onClickPauseOrder, }: PublicWaitPrompProps): React.JSX.Element => { const { t } = useTranslation(); - const { federation } = useContext(FederationContext); + const { federation, federationUpdatedAt } = useContext(FederationContext); const currencyCode = currencies[order.currency.toString()]; @@ -51,7 +51,7 @@ export const PublicWaitPrompt = ({ bookOrder?.currency == order.currency && order.status == 1 ); return similarOrders.length; - }, [federation.book]); + }, [federationUpdatedAt]); const premiumPercentile = useMemo((): number => { const orders = Object.values(federation.book) ?? []; @@ -68,7 +68,7 @@ export const PublicWaitPrompt = ({ const percentile = (sumPremium / premiums.length) * 100; return Math.floor(parseFloat(percentile.toFixed(2))); - }, [federation.book]); + }, [federationUpdatedAt]); return (