from celery import shared_task 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 from decouple import config @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() accounted_yesterday = AccountingDay.objects.latest('day') except: last_accounted_day = None accounted_yesterday = 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 = {} 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)) # Coarse accounting based on LNpayment objects 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, inflow = inflow, outflow = outflow, routing_fees = routing_fees, cashflow = inflow - outflow - routing_fees, rewards_claimed = rewards_claimed, ) # Fine Net Daily accounting based on orders # Only account for orders where everything worked out right payouts = day_payments.filter(type=LNPayment.Types.NORM,concept=LNPayment.Concepts.PAYBUYER, status=LNPayment.Status.SUCCED) escrows_settled = 0 payouts_paid = 0 routing_cost = 0 for payout in payouts: escrows_settled += payout.order_paid.trade_escrow.num_satoshis payouts_paid += payout.num_satoshis routing_cost += payout.fee # account for those orders where bonds were lost # + Settled bonds / bond_split bonds_settled = day_payments.filter(type=LNPayment.Types.HOLD,concept__in=[LNPayment.Concepts.TAKEBOND,LNPayment.Concepts.MAKEBOND], status=LNPayment.Status.SETLED) if len(bonds_settled) > 0: collected_slashed_bonds = (bonds_settled.aggregate(Sum('num_satoshis'))['num_satoshis__sum'])* float(config('SLASHED_BOND_REWARD_SPLIT')) else: collected_slashed_bonds = 0 accounted_day.net_settled = escrows_settled + collected_slashed_bonds accounted_day.net_paid = payouts_paid + routing_cost accounted_day.net_balance = float(accounted_day.net_settled) - float(accounted_day.net_paid) # Differential accounting based on change of outstanding states and disputes unreslved 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 else: outstanding_pending_disputes = 0 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.disputes = outstanding_pending_disputes - accounted_yesterday.outstanding_earned_rewards # Close the loop accounted_day.save() accounted_yesterday = accounted_day result[str(day)]={'contracted':contracted,'inflow':inflow,'outflow':outflow} day = day + timedelta(days=1) return result