mirror of
https://github.com/RoboSats/robosats.git
synced 2025-09-06 18:04:10 +00:00
Convert cached currency to relation children of order
This commit is contained in:
10
api/admin.py
10
api/admin.py
@ -2,7 +2,7 @@ from django.contrib import admin
|
|||||||
from django_admin_relation_links import AdminChangeLinksMixin
|
from django_admin_relation_links import AdminChangeLinksMixin
|
||||||
from django.contrib.auth.models import Group, User
|
from django.contrib.auth.models import Group, User
|
||||||
from django.contrib.auth.admin import UserAdmin
|
from django.contrib.auth.admin import UserAdmin
|
||||||
from .models import Order, LNPayment, Profile, MarketTick, CachedExchangeRate
|
from .models import Order, LNPayment, Profile, MarketTick, Currency
|
||||||
|
|
||||||
admin.site.unregister(Group)
|
admin.site.unregister(Group)
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
@ -24,9 +24,9 @@ class EUserAdmin(UserAdmin):
|
|||||||
|
|
||||||
@admin.register(Order)
|
@admin.register(Order)
|
||||||
class OrderAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
class OrderAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
||||||
list_display = ('id','type','maker_link','taker_link','status','amount','currency','t0_satoshis','is_disputed','is_fiat_sent','created_at','expires_at', 'buyer_invoice_link','maker_bond_link','taker_bond_link','trade_escrow_link')
|
list_display = ('id','type','maker_link','taker_link','status','amount','currency_link','t0_satoshis','is_disputed','is_fiat_sent','created_at','expires_at', 'buyer_invoice_link','maker_bond_link','taker_bond_link','trade_escrow_link')
|
||||||
list_display_links = ('id','type')
|
list_display_links = ('id','type')
|
||||||
change_links = ('maker','taker','buyer_invoice','maker_bond','taker_bond','trade_escrow')
|
change_links = ('maker','taker','currency','buyer_invoice','maker_bond','taker_bond','trade_escrow')
|
||||||
list_filter = ('is_disputed','is_fiat_sent','type','currency','status')
|
list_filter = ('is_disputed','is_fiat_sent','type','currency','status')
|
||||||
|
|
||||||
@admin.register(LNPayment)
|
@admin.register(LNPayment)
|
||||||
@ -43,8 +43,8 @@ class UserProfileAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
|
|||||||
change_links =['user']
|
change_links =['user']
|
||||||
readonly_fields = ['avatar_tag']
|
readonly_fields = ['avatar_tag']
|
||||||
|
|
||||||
@admin.register(CachedExchangeRate)
|
@admin.register(Currency)
|
||||||
class CachedExchangeRateAdmin(admin.ModelAdmin):
|
class CurrencieAdmin(admin.ModelAdmin):
|
||||||
list_display = ('currency','exchange_rate','timestamp')
|
list_display = ('currency','exchange_rate','timestamp')
|
||||||
readonly_fields = ('currency','exchange_rate','timestamp')
|
readonly_fields = ('currency','exchange_rate','timestamp')
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ from datetime import time, timedelta
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from .lightning.node import LNNode
|
from .lightning.node import LNNode
|
||||||
|
|
||||||
from .models import Order, LNPayment, MarketTick, User, CachedExchangeRate
|
from .models import Order, LNPayment, MarketTick, User, Currency
|
||||||
from decouple import config
|
from decouple import config
|
||||||
|
|
||||||
import math
|
import math
|
||||||
@ -72,7 +72,7 @@ class Logics():
|
|||||||
if order.is_explicit:
|
if order.is_explicit:
|
||||||
satoshis_now = order.satoshis
|
satoshis_now = order.satoshis
|
||||||
else:
|
else:
|
||||||
exchange_rate = float(CachedExchangeRate.objects.get(currency=order.currency).exchange_rate)
|
exchange_rate = float(order.currency.exchange_rate)
|
||||||
premium_rate = exchange_rate * (1+float(order.premium)/100)
|
premium_rate = exchange_rate * (1+float(order.premium)/100)
|
||||||
satoshis_now = (float(order.amount) / premium_rate) * 100*1000*1000
|
satoshis_now = (float(order.amount) / premium_rate) * 100*1000*1000
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ class Logics():
|
|||||||
|
|
||||||
def price_and_premium_now(order):
|
def price_and_premium_now(order):
|
||||||
''' computes order premium live '''
|
''' computes order premium live '''
|
||||||
exchange_rate = float(CachedExchangeRate.objects.get(currency=order.currency).exchange_rate)
|
exchange_rate = float(order.currency.exchange_rate)
|
||||||
if not order.is_explicit:
|
if not order.is_explicit:
|
||||||
premium = order.premium
|
premium = order.premium
|
||||||
price = exchange_rate * (1+float(premium)/100)
|
price = exchange_rate * (1+float(premium)/100)
|
||||||
@ -373,7 +373,7 @@ class Logics():
|
|||||||
order.last_satoshis = cls.satoshis_now(order)
|
order.last_satoshis = cls.satoshis_now(order)
|
||||||
bond_satoshis = int(order.last_satoshis * BOND_SIZE)
|
bond_satoshis = int(order.last_satoshis * BOND_SIZE)
|
||||||
pos_text = 'Buying' if cls.is_buyer(order, user) else 'Selling'
|
pos_text = 'Buying' if cls.is_buyer(order, user) else 'Selling'
|
||||||
description = (f"RoboSats - Taking 'Order {order.id}' {pos_text} BTC for {str(float(order.amount)) + Order.currency_dict[str(order.currency)]}"
|
description = (f"RoboSats - Taking 'Order {order.id}' {pos_text} BTC for {str(float(order.amount)) + str(order.currency)}"# Order.currency_dict[str(order.currency)]}"
|
||||||
+ " - This is a taker bond, it will freeze in your wallet temporarily and automatically return. It will be charged if you cheat or cancel.")
|
+ " - This is a taker bond, it will freeze in your wallet temporarily and automatically return. It will be charged if you cheat or cancel.")
|
||||||
|
|
||||||
# Gen hold Invoice
|
# Gen hold Invoice
|
||||||
|
@ -15,6 +15,24 @@ MAX_TRADE = int(config('MAX_TRADE'))
|
|||||||
FEE = float(config('FEE'))
|
FEE = float(config('FEE'))
|
||||||
BOND_SIZE = float(config('BOND_SIZE'))
|
BOND_SIZE = float(config('BOND_SIZE'))
|
||||||
|
|
||||||
|
|
||||||
|
class Currency(models.Model):
|
||||||
|
|
||||||
|
currency_dict = json.load(open('./frontend/static/assets/currencies.json'))
|
||||||
|
currency_choices = [(int(val), label) for val, label in list(currency_dict.items())]
|
||||||
|
|
||||||
|
currency = models.PositiveSmallIntegerField(choices=currency_choices, null=False, unique=True)
|
||||||
|
exchange_rate = models.DecimalField(max_digits=10, decimal_places=2, default=None, null=True, validators=[MinValueValidator(0)])
|
||||||
|
timestamp = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
# returns currency label ( 3 letters code)
|
||||||
|
return self.currency_dict[str(self.currency)]
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'Cached market currency'
|
||||||
|
verbose_name_plural = 'Currencies'
|
||||||
|
|
||||||
class LNPayment(models.Model):
|
class LNPayment(models.Model):
|
||||||
|
|
||||||
class Types(models.IntegerChoices):
|
class Types(models.IntegerChoices):
|
||||||
@ -62,6 +80,10 @@ class LNPayment(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return (f'LN-{str(self.id)[:8]}: {self.Concepts(self.concept).label} - {self.Status(self.status).label}')
|
return (f'LN-{str(self.id)[:8]}: {self.Concepts(self.concept).label} - {self.Status(self.status).label}')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'Lightning payment'
|
||||||
|
verbose_name_plural = 'Lightning payments'
|
||||||
|
|
||||||
class Order(models.Model):
|
class Order(models.Model):
|
||||||
|
|
||||||
class Types(models.IntegerChoices):
|
class Types(models.IntegerChoices):
|
||||||
@ -88,9 +110,6 @@ class Order(models.Model):
|
|||||||
MLD = 16, 'Maker lost dispute'
|
MLD = 16, 'Maker lost dispute'
|
||||||
TLD = 17, 'Taker lost dispute'
|
TLD = 17, 'Taker lost dispute'
|
||||||
|
|
||||||
currency_dict = json.load(open('./frontend/static/assets/currencies.json'))
|
|
||||||
currency_choices = [(int(val), label) for val, label in list(currency_dict.items())]
|
|
||||||
|
|
||||||
# order info
|
# order info
|
||||||
status = models.PositiveSmallIntegerField(choices=Status.choices, null=False, default=Status.WFB)
|
status = models.PositiveSmallIntegerField(choices=Status.choices, null=False, default=Status.WFB)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
@ -98,7 +117,7 @@ class Order(models.Model):
|
|||||||
|
|
||||||
# order details
|
# order details
|
||||||
type = models.PositiveSmallIntegerField(choices=Types.choices, null=False)
|
type = models.PositiveSmallIntegerField(choices=Types.choices, null=False)
|
||||||
currency = models.PositiveSmallIntegerField(choices=currency_choices, null=False)
|
currency = models.ForeignKey(Currency, null=True, on_delete=models.SET_NULL)
|
||||||
amount = models.DecimalField(max_digits=9, decimal_places=4, validators=[MinValueValidator(0.00001)])
|
amount = models.DecimalField(max_digits=9, decimal_places=4, validators=[MinValueValidator(0.00001)])
|
||||||
payment_method = models.CharField(max_length=35, null=False, default="not specified", blank=True)
|
payment_method = models.CharField(max_length=35, null=False, default="not specified", blank=True)
|
||||||
|
|
||||||
@ -155,7 +174,7 @@ class Order(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# Make relational back to ORDER
|
# Make relational back to ORDER
|
||||||
return (f'Order {self.id}: {self.Types(self.type).label} BTC for {float(self.amount)} {self.currency_dict[str(self.currency)]}')
|
return (f'Order {self.id}: {self.Types(self.type).label} BTC for {float(self.amount)} {self.currency}')
|
||||||
|
|
||||||
@receiver(pre_delete, sender=Order)
|
@receiver(pre_delete, sender=Order)
|
||||||
def delete_lnpayment_at_order_deletion(sender, instance, **kwargs):
|
def delete_lnpayment_at_order_deletion(sender, instance, **kwargs):
|
||||||
@ -219,13 +238,6 @@ class Profile(models.Model):
|
|||||||
def avatar_tag(self):
|
def avatar_tag(self):
|
||||||
return mark_safe('<img src="%s" width="50" height="50" />' % self.get_avatar())
|
return mark_safe('<img src="%s" width="50" height="50" />' % self.get_avatar())
|
||||||
|
|
||||||
class CachedExchangeRate(models.Model):
|
|
||||||
|
|
||||||
currency = models.PositiveSmallIntegerField(choices=Order.currency_choices, null=False, unique=True)
|
|
||||||
exchange_rate = models.DecimalField(max_digits=10, decimal_places=2, default=None, null=True, validators=[MinValueValidator(0)])
|
|
||||||
timestamp = models.DateTimeField(auto_now_add=True)
|
|
||||||
|
|
||||||
|
|
||||||
class MarketTick(models.Model):
|
class MarketTick(models.Model):
|
||||||
'''
|
'''
|
||||||
Records tick by tick Non-KYC Bitcoin price.
|
Records tick by tick Non-KYC Bitcoin price.
|
||||||
@ -242,7 +254,7 @@ class MarketTick(models.Model):
|
|||||||
price = models.DecimalField(max_digits=10, decimal_places=2, default=None, null=True, validators=[MinValueValidator(0)])
|
price = models.DecimalField(max_digits=10, decimal_places=2, default=None, null=True, validators=[MinValueValidator(0)])
|
||||||
volume = models.DecimalField(max_digits=8, decimal_places=8, default=None, null=True, validators=[MinValueValidator(0)])
|
volume = models.DecimalField(max_digits=8, decimal_places=8, default=None, null=True, validators=[MinValueValidator(0)])
|
||||||
premium = models.DecimalField(max_digits=5, decimal_places=2, default=None, null=True, validators=[MinValueValidator(-100), MaxValueValidator(999)], blank=True)
|
premium = models.DecimalField(max_digits=5, decimal_places=2, default=None, null=True, validators=[MinValueValidator(-100), MaxValueValidator(999)], blank=True)
|
||||||
currency = models.PositiveSmallIntegerField(choices=Order.currency_choices, null=True)
|
currency = models.PositiveSmallIntegerField(choices=Currency.currency_choices, null=True)
|
||||||
timestamp = models.DateTimeField(auto_now_add=True)
|
timestamp = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
# Relevant to keep record of the historical fee, so the insight on the premium can be better analyzed
|
# Relevant to keep record of the historical fee, so the insight on the premium can be better analyzed
|
||||||
@ -259,7 +271,7 @@ class MarketTick(models.Model):
|
|||||||
elif order.taker_bond.status == LNPayment.Status.LOCKED:
|
elif order.taker_bond.status == LNPayment.Status.LOCKED:
|
||||||
volume = order.last_satoshis / 100000000
|
volume = order.last_satoshis / 100000000
|
||||||
price = float(order.amount) / volume # Amount Fiat / Amount BTC
|
price = float(order.amount) / volume # Amount Fiat / Amount BTC
|
||||||
market_exchange_rate = float(CachedExchangeRate.objects.get(currency=order.currency).exchange_rate)
|
market_exchange_rate = float(order.currency.exchange_rate)
|
||||||
premium = 100 * (price / market_exchange_rate - 1)
|
premium = 100 * (price / market_exchange_rate - 1)
|
||||||
|
|
||||||
tick = MarketTick.objects.create(
|
tick = MarketTick.objects.create(
|
||||||
@ -273,4 +285,7 @@ class MarketTick(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'Tick: {str(self.id)[:8]}'
|
return f'Tick: {str(self.id)[:8]}'
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'Market tick'
|
||||||
|
verbose_name_plural = 'Market ticks'
|
||||||
|
|
||||||
|
11
api/tasks.py
11
api/tasks.py
@ -2,7 +2,7 @@ from celery import shared_task
|
|||||||
|
|
||||||
from .lightning.node import LNNode
|
from .lightning.node import LNNode
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from .models import LNPayment, Order, CachedExchangeRate
|
from .models import LNPayment, Order, Currency
|
||||||
from .logics import Logics
|
from .logics import Logics
|
||||||
from .utils import get_exchange_rates
|
from .utils import get_exchange_rates
|
||||||
|
|
||||||
@ -55,14 +55,15 @@ def query_all_lnd_invoices():
|
|||||||
|
|
||||||
@shared_task(name="cache_market", ignore_result=True)
|
@shared_task(name="cache_market", ignore_result=True)
|
||||||
def cache_market():
|
def cache_market():
|
||||||
exchange_rates = get_exchange_rates(list(Order.currency_dict.values()))
|
exchange_rates = get_exchange_rates(list(Currency.currency_dict.values()))
|
||||||
results = {}
|
results = {}
|
||||||
for val in Order.currency_dict:
|
for val in Currency.currency_dict:
|
||||||
rate = exchange_rates[int(val)-1] # currecies are indexed starting at 1 (USD)
|
rate = exchange_rates[int(val)-1] # currecies are indexed starting at 1 (USD)
|
||||||
results[val] = {Order.currency_dict[val], rate}
|
results[val] = {Currency.currency_dict[val], rate}
|
||||||
|
|
||||||
# Create / Update database cached prices
|
# Create / Update database cached prices
|
||||||
CachedExchangeRate.objects.update_or_create(
|
Currency.objects.update_or_create(
|
||||||
|
id = int(val),
|
||||||
currency = int(val),
|
currency = int(val),
|
||||||
# if there is a Cached Exchange rate matching that value, it updates it with defaults below
|
# if there is a Cached Exchange rate matching that value, it updates it with defaults below
|
||||||
defaults = {
|
defaults = {
|
||||||
|
@ -9,7 +9,7 @@ from django.contrib.auth import authenticate, login, logout
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
from .serializers import ListOrderSerializer, MakeOrderSerializer, UpdateOrderSerializer
|
from .serializers import ListOrderSerializer, MakeOrderSerializer, UpdateOrderSerializer
|
||||||
from .models import LNPayment, MarketTick, Order
|
from .models import LNPayment, MarketTick, Order, Currency
|
||||||
from .logics import Logics
|
from .logics import Logics
|
||||||
from .utils import get_lnd_version, get_commit_robosats
|
from .utils import get_lnd_version, get_commit_robosats
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ class MakerView(CreateAPIView):
|
|||||||
# Creates a new order
|
# Creates a new order
|
||||||
order = Order(
|
order = Order(
|
||||||
type=type,
|
type=type,
|
||||||
currency=currency,
|
currency=Currency.objects.get(id=currency),
|
||||||
amount=amount,
|
amount=amount,
|
||||||
payment_method=payment_method,
|
payment_method=payment_method,
|
||||||
premium=premium,
|
premium=premium,
|
||||||
|
Reference in New Issue
Block a user