Init accounting panel, add import/export

This commit is contained in:
Reckless_Satoshi 2022-03-20 16:32:25 -07:00
parent 5730ec0383
commit ca79ea9914
No known key found for this signature in database
GPG Key ID: 9C4585B561315571
14 changed files with 171 additions and 36 deletions

3
.gitignore vendored
View File

@ -651,4 +651,5 @@ api/lightning/invoices*
api/lightning/router*
api/lightning/googleapis*
frontend/static/admin*
frontend/static/rest_framework*
frontend/static/rest_framework*
frontend/static/import_export*

View File

@ -78,6 +78,7 @@ class LNPaymentAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
"concept",
"status",
"num_satoshis",
"fee",
"type",
"expires_at",
"expiry_height",
@ -133,7 +134,6 @@ class CurrencieAdmin(admin.ModelAdmin):
readonly_fields = ("currency", "exchange_rate", "timestamp")
ordering = ("id", )
@admin.register(MarketTick)
class MarketTickAdmin(admin.ModelAdmin):
list_display = ("timestamp", "price", "volume", "premium", "currency",

View File

@ -261,6 +261,7 @@ class LNNode:
if response.status == 2: # STATUS 'SUCCEEDED'
lnpayment.status = LNPayment.Status.SUCCED
lnpayment.fee = float(response.fee_msat)/1000
lnpayment.save()
return True, None

View File

@ -440,6 +440,12 @@ class Logics:
"bad_request":
"You cannot submit a invoice while bonds are not locked."
}
if order.status == Order.Status.FAI:
if order.payout.status != LNPayment.Status.EXPIRE:
return False, {
"bad_request":
"You cannot submit an invoice only after expiration or 3 failed attempts"
}
num_satoshis = cls.payout_amount(order, user)[1]["invoice_amount"]
payout = LNNode.validate_ln_invoice(invoice, num_satoshis)

View File

@ -109,6 +109,8 @@ class LNPayment(models.Model):
MinValueValidator(100),
MaxValueValidator(MAX_TRADE * (1 + DEFAULT_BOND_SIZE + FEE)),
])
# Fee in sats with mSats decimals fee_msat
fee = models.DecimalField(max_digits=10, decimal_places=3, default=0, null=False, blank=False)
created_at = models.DateTimeField()
expires_at = models.DateTimeField()
cltv_expiry = models.PositiveSmallIntegerField(null=True,

View File

@ -123,6 +123,7 @@ def follow_send_payment(lnpayment):
if response.status == 2: # Status 2 'SUCCEEDED'
print("SUCCEEDED")
lnpayment.status = LNPayment.Status.SUCCED
lnpayment.fee = float(response.fee_msat)/1000
lnpayment.save()
order.status = Order.Status.SUC
order.expires_at = timezone.now() + timedelta(

View File

@ -1,41 +1,53 @@
from django.contrib import admin
from control.models import AccountingDay, AccountingMonth, Dispute
from import_export.admin import ImportExportModelAdmin
# Register your models here.
@admin.register(AccountingDay)
class AccountingDayAdmin(admin.ModelAdmin):
class AccountingDayAdmin(ImportExportModelAdmin):
list_display = (
"day",
"contracted",
"num_contracts",
"net_settled",
"net_paid",
"net_balance",
"total_inflow",
"total_outflow",
"total_routing_fees",
"total_cashflow",
"pending_rewards",
"inflow",
"outflow",
"routing_fees",
"cashflow",
"outstanding_earned_rewards",
"outstanding_pending_disputes",
"lifetime_rewards_claimed",
"outstanding_earned_rewards",
"pending_disputes",
"pending_claimable",
"rewards_claimed",
)
change_links = ["day"]
search_fields = ["day"]
@admin.register(AccountingMonth)
class AccountingMonthAdmin(admin.ModelAdmin):
class AccountingMonthAdmin(ImportExportModelAdmin):
list_display = (
"month",
"contracted",
"num_contracts",
"net_settled",
"net_paid",
"net_balance",
"total_inflow",
"total_outflow",
"total_routing_fees",
"total_cashflow",
"pending_rewards",
"inflow",
"outflow",
"routing_fees",
"cashflow",
"outstanding_earned_rewards",
"outstanding_pending_disputes",
"lifetime_rewards_claimed",
"outstanding_earned_rewards",
"pending_disputes",
"pending_claimable",
"rewards_claimed",
)
change_links = ["month"]
search_fields = ["month"]

View File

@ -7,6 +7,8 @@ class AccountingDay(models.Model):
# Every field is denominated in Sats with (3 decimals for millisats)
# Total volume contracted
contracted = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Number of contracts
num_contracts = models.BigIntegerField(default=0, null=False, blank=False)
# Net volume of trading invoices settled (excludes disputes)
net_settled = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Net volume of trading invoices paid (excludes rewards and disputes)
@ -14,19 +16,26 @@ class AccountingDay(models.Model):
# Sum of net settled and net paid
net_balance = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Total volume of invoices settled
total_inflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
inflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Total volume of invoices paid
total_outflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
outflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Total cost in routing fees
total_routing_fees = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
routing_fees = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Total inflows minus outflows and routing fees
total_cashflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Balance on pending rewards (referral rewards and slashed bonds)
pending_rewards = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
cashflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Balance on earned rewards (referral rewards, slashed bonds and solved disputes)
outstanding_earned_rewards = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Balance on pending disputes (not resolved yet)
outstanding_pending_disputes = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Rewards claimed lifetime
lifetime_rewards_claimed = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Balance change from last day on earned rewards (referral rewards, slashed bonds and solved disputes)
earned_rewards = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Balance change on pending disputes (not resolved yet)
pending_disputes = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Balance on pending rewards and resolved disputes
pending_claimable = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Rewards claimed on day
rewards_claimed = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
class AccountingMonth(models.Model):
@ -35,6 +44,8 @@ class AccountingMonth(models.Model):
# Every field is denominated in Sats with (3 decimals for millisats)
# Total volume contracted
contracted = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Number of contracts
num_contracts = models.BigIntegerField(default=0, null=False, blank=False)
# Net volume of trading invoices settled (excludes disputes)
net_settled = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Net volume of trading invoices paid (excludes rewards and disputes)
@ -42,19 +53,25 @@ class AccountingMonth(models.Model):
# Sum of net settled and net paid
net_balance = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Total volume of invoices settled
total_inflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
inflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Total volume of invoices paid
total_outflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
outflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Total cost in routing fees
total_routing_fees = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
routing_fees = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Total inflows minus outflows and routing fees
total_cashflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Balance on pending rewards (referral rewards and slashed bonds)
pending_rewards = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
cashflow = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Balance on earned rewards (referral rewards, slashed bonds and solved disputes)
outstanding_earned_rewards = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Balance on pending disputes (not resolved yet)
outstanding_pending_disputes = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Rewards claimed lifetime
lifetime_rewards_claimed = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Balance change from last day on earned rewards (referral rewards, slashed bonds and solved disputes)
earned_rewards = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Balance change on pending disputes (not resolved yet)
pending_disputes = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Balance on pending rewards and resolved disputes
pending_claimable = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
# Rewards claimed on day
rewards_claimed = models.DecimalField(max_digits=15, decimal_places=3, default=0, null=False, blank=False)
class Dispute(models.Model):
pass

View File

@ -1,6 +1,95 @@
from celery import shared_task
from api.models import Order, LNPayment, Profile
from api.models import Order, LNPayment, Profile, MarketTick
from control.models import AccountingDay, AccountingMonth
from django.utils import timezone
from datetime import timedelta
from django.db.models import Sum
@shared_task(name="do_accounting")
def do_accounting():
'''
Does all accounting from the beginning of time
'''
all_payments = LNPayment.objects.all()
all_ticks = MarketTick.objects.all()
today = timezone.now().date()
try:
last_accounted_day = AccountingDay.objects.latest('day').day.date()
except:
last_accounted_day = None
if last_accounted_day == today:
return {'message':'no days to account for'}
elif last_accounted_day != None:
initial_day = last_accounted_day + timedelta(days=1)
elif last_accounted_day == None:
initial_day = all_payments.earliest('created_at').created_at.date()
day = initial_day
result = {}
accounted_yesterday = None
while day <= today:
day_payments = all_payments.filter(created_at__gte=day,created_at__lte=day+timedelta(days=1))
day_ticks = all_ticks.filter(timestamp__gte=day,timestamp__lte=day+timedelta(days=1))
contracted = day_ticks.aggregate(Sum('volume'))['volume__sum']
num_contracts = day_ticks.count()
inflow = day_payments.filter(type=LNPayment.Types.HOLD,status=LNPayment.Status.SETLED).aggregate(Sum('num_satoshis'))['num_satoshis__sum']
outflow = day_payments.filter(type=LNPayment.Types.NORM,status=LNPayment.Status.SUCCED).aggregate(Sum('num_satoshis'))['num_satoshis__sum']
routing_fees = day_payments.filter(type=LNPayment.Types.NORM,status=LNPayment.Status.SUCCED).aggregate(Sum('fee'))['fee__sum']
rewards_claimed = day_payments.filter(type=LNPayment.Types.NORM,concept=LNPayment.Concepts.WITHREWA,status=LNPayment.Status.SUCCED).aggregate(Sum('num_satoshis'))['num_satoshis__sum']
contracted = 0 if contracted == None else contracted
inflow = 0 if inflow == None else inflow
outflow = 0 if outflow == None else outflow
routing_fees = 0 if routing_fees == None else routing_fees
rewards_claimed = 0 if rewards_claimed == None else rewards_claimed
accounted_day = AccountingDay.objects.create(
day = day,
contracted = contracted,
num_contracts = num_contracts,
net_settled = 0,
net_paid = 0,
net_balance = 0,
inflow = inflow,
outflow = outflow,
routing_fees = routing_fees,
cashflow = inflow - outflow - routing_fees,
rewards_claimed = rewards_claimed,
)
if day == today:
pending_disputes = Order.objects.filter(status__in=[Order.Status.DIS,Order.Status.WFR])
if len(pending_disputes) > 0:
outstanding_pending_disputes = 0
for order in pending_disputes:
outstanding_pending_disputes += order.payout.num_satoshis
accounted_day.outstanding_earned_rewards = Profile.objects.all().aggregate(Sum('earned_rewards'))['earned_rewards__sum']
accounted_day.outstanding_pending_disputes = outstanding_pending_disputes
accounted_day.lifetime_rewards_claimed = Profile.objects.all().aggregate(Sum('claimed_rewards'))['claimed_rewards__sum']
if accounted_yesterday != None:
accounted_day.earned_rewards = accounted_day.outstanding_earned_rewards - accounted_yesterday.outstanding_earned_rewards
accounted_day.pending_disputes = outstanding_pending_disputes - accounted_yesterday.outstanding_earned_rewards
accounted_day.save()
accounted_yesterday = accounted_day
result[str(day)]={'contracted':contracted,'inflow':inflow,'outflow':outflow}
day = day + timedelta(days=1)
return result
@shared_task(name="account_day")
def account_day():
'''
Does daily accounting since last accounted day.
To be run daily.
'''
return

View File

@ -350,7 +350,7 @@ export default class MakerPage extends Component {
<Grid item xs={12} align="center" spacing={1}>
<FormControl align="center">
<FormHelperText>
<Tooltip enterTouchDelay="0" title={"Set the skin-in-the-game (increase for higher safety assurance)"}>
<Tooltip enterTouchDelay="0" placement="top" title={"Set the skin-in-the-game (increase for higher safety assurance)"}>
<div align="center" style={{display:'flex',flexWrap:'wrap', transform: 'translate(20%, 0)'}}>
Fidelity Bond Size <LockIcon sx={{height:20,width:20}}/>
</div>

File diff suppressed because one or more lines are too long

View File

@ -23,4 +23,5 @@ scipy==1.8.0
gunicorn==20.1.0
psycopg2==2.9.3
SQLAlchemy==1.4.31
django-import-export==2.7.1
requests[socks]

View File

@ -39,6 +39,10 @@ app.conf.beat_schedule = {
"task": "give_rewards",
"schedule": crontab(hour=0, minute=0),
},
"account-day": { # Does accounting for the last day
"task": "account_day",
"schedule": crontab(hour=23, minute=55),
},
"cache-market-prices": { # Cache market prices every minute
"task": "cache_external_market_prices",
"schedule": timedelta(seconds=60),

View File

@ -56,6 +56,7 @@ INSTALLED_APPS = [
"channels",
"django_celery_beat",
"django_celery_results",
"import_export",
"api",
"chat",
"control",
@ -74,6 +75,7 @@ MIDDLEWARE = [
]
ROOT_URLCONF = "robosats.urls"
IMPORT_EXPORT_USE_TRANSACTIONS = True
TEMPLATES = [
{
@ -92,7 +94,6 @@ TEMPLATES = [
]
WSGI_APPLICATION = "robosats.wsgi.application"
USE_TZ = True
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases