Django recurring record/payment calculation - python

I have a table of "expected payments" that either are a one-time payment, or recurring monthly, quarterly or yearly. The goal is to calculate the total amount of expected payments for each of the upcoming 12 months.
The model looks like:
class ExpectedPayment(models.Model):
id = models.BigIntegerField(primary_key=True)
period = models.BigIntegerField()
date_start = models.DateField(blank=True, null=True)
date_end = models.DateField(blank=True, null=True)
amount = models.DecimalField(max_digits=1000, decimal_places=1000)
Period 1 = Monthly recurring
Period 2 = Every Quarter - Following the start date. (So started on February, next quarter is May)
Period 3 = Yearly recurring - Also following the start date
Period 4 = One time payment
How can I properly calculate the total "amount" for each month, while taking into account when a payment should take place? Especially the quarterly payments..
I can't figure out how to incorporate when a payment has been started and ended, as it might have been started halfway throughout the year, ended 3 months later or was already finished in the first place. How do I know which month the quarterly should count as an expected payment, when it is always relative to its start_date.
An example for a single month: (every monthly payment that is active in the current month)
start_of_current_month = datetime.today().replace(day=1)
current_month_expected_monthly_payments = ExpectedPayment.objects.filter(period=1, date_start__lte=start_of_current_month, date_end__gte=start_of_current_month).aggregate(total=Sum("amount"))

Assuming for every given period the payment is collected at the beginning of the period. For example - For quarterly payment beginning in February the amount of February, March and April is collected in February itself. For reporting, you want to report the amount as amount/3 since you want to report on monthly basis. Here is what I think -
For every period you can calculate the amount by using annotation -
ExpectedPayment.objects.filter(
date_start__lte=start_of_current_month,
date_end__gte=start_of_current_month
).annotate(
monthly_amount = Case(
When(period=2, then=F('amount')/3,
When(period=3, then=F('amount')/12,
default=F('amount')
)).aggregate(monthly_sum = Sum(F('monthly_amount'))
You would not need a separate case for monthly payments since you are reporting amount monthly. For one time payments, the amount will be considered if the transaction took place in that month. Therefore, no separate cases needed for those cases.
I referred to the documentation here:
Conditional expressions
Query expressions
Here is a issue that uses annotation and aggregation which I think you might find useful.

Related

Python: Utility function to determine when to roll over to next year (or roll back to previous year) for a given date and grace period

I'm writing code for a birthday gift website. I have a routine that given a birthdate (in mm/dd/yyyy date format) needs to return back the birthdate for the current year, based on when someone purchases the gift.
It seems simple enough, but there is a complication. The routine needs to effectively account for the fact that people buy gifts a few months in advance, or a few months late, and that the year changes over in the meantime.
For example, if someone's birthdate is 12/29/1975 (Dec 29th) and the current date is 01/01/2023 (1st Jan), it is more likely someone is buying the gift for last year's birthday (i.e. 12/29/2022) than for this year's birthday (12/29/2023). Similarly if someone's birthday is on Jan 5th, and today's date happens to be November 29th, 2022, it is more likely I am buying the gift for that person's next birthday (i.e. 01/05/2023) than the current year's past birthday.
So you need to be able to clock the year forward in certain cases, as well as back in certain cases, and need to incorporate some kind of arbitrary grace period that determines when to clock forward or back.
By creating lots of different use cases and through trial and error I came up with the following function. It works (or at least passes all my use cases) but I'm struggling for an intuitive understanding of why it works, or whether there is a more elegant / intuitive way to do this. Also I happen to be paranoid that there may be cases for which this may fail and produce faulty results!
Any thoughts / insight would be much appreciated!
def determine_correct_year_to_use_for_birthdays(birthday, today=None, grace_period=120):
"""
Determine the correct year to use for a date. After grace_period number of days, you can assume the birthday is for the
next year. Have to account for year changeovers also.
:param birthday:
:param grace_period: in days
:return:
# covert birthdate to this year's birthday
# if (birthday - current date) is positive AND diff > (365 - grace period), go back one year
# if (birthday - current date) is positive AND diff < (365 - grace period), keep current year
# if (birthday - current date) is negative AND abs(diff) < (365 - grace period), keep current year
# if (birthday - current date) is negative AND abs(diff) > (365 - grace period), move to next year
"""
if not today:
today = datetime.now()
birthday = birthday.replace(year=today.year)
diff = (birthday - today).days
if diff > 0 and abs(diff) > (365 - grace_period):
birthday = birthday.replace(year=today.year - 1)
if diff < 0 and abs(diff) > (365 - grace_period):
birthday = birthday.replace(year=today.year + 1)
return birthday

I am given a ledger. I want to know how many days it took every bills to gets settled

I am given a ledger. I want to know how many days it took every bills to gets settled.
I am running two loops. One for every bill and another for adding all the settled amounts. If the amounts just match, I want to copy the last date of the last payment to calculate the gap between billing date and settlement date.
Credit = 0 Balance = 0
for i in range(len(df.VOUCHER_NO)):
for j in range(len(df.VOUCHER_NO)):
if df.DEBIT[i] + Balance < Credit:
Credit = Credit + df.CREDIT[j]
df.CREDIT[j] = 0
df.Date[i] = ''
else:
df.Date[i] = df.DOC_DATE[j]
Balance = Balance + df.DEBIT[i] - Credit
But it is copying some date date that I don't understand.
I suspect in your df.VOUCHER_NO, Date was initialized as date. When in your loop you put df.Date[j]='', it will consider that you are you are initializing date to the beginning of the EPOCH which is 1970-01-01 00:00:00
Read this doc for better understanding of date in python : https://www.javatpoint.com/python-epoch-to-datetime

Where and how to calculate total price depending on date range user selected?

i'm working on site for renting rooms. User picks 2 dates(UserStartDate & UserEndDate).
with this python code i gonna get number of days in his date range:
user_date_range = [endUser - timedelta(i) for i in range((endUser - startUser).days+1)]
user_range_num_days = len(user_date_range)
and i have a day price for room: 20$
but due to lack of proficiency in Django,I can't figure out how to calculate user price according to his date range. And where it should be done.
hope for your help.
It doesn't have anything to do with django but rather python. I assume user_start_date and user_end_date are both python datetime.date or datetime.datetime objects, then you could do:
num_days = (user_end_date - user_start_date).days
total_price = num_days * 20
https://docs.python.org/2/library/calendar.html
A calendar is necessary as you should be aware that not all months have the same amount of days in them. itermonthdates(year, month) returns an iterator for all days in the month. Run through that iterator and increment a count for every date match within the range. Of course if the end date extends into the next month keep the same counter.

Linking Simpy simulation time to Python Calendar for week day specific actions

I want to build a simulation model of a production network with SimPy comprising the following features with regard to time:
Plants work from Monday to Friday (with two shifts of 8 hours)
Heavy trucks drive on all days of the week except Sunday
Light trucks drive on all days of the week, including Sunday
To this purpose, I want to construct a BroadcastPipe as given in the docs combined with timeouts to make the objects wait during days they are not working (for the plants additional logic is required to model shifts). This BroadcastPipe would just count the days (assuming 24*60 minutes for each day) and then say "It's Monday, everybody". The objects (plant, light and heavy trucks) would then process this information individually and act accordingly.
Now, I wonder whether there is an elegant method to link simulation time to regular Python Calender objects in order to easily access days of the week. This would be useful for clarity and enhancements like bank holidays and varying starting days. Do you have any advise how to do this? (or general advice on how to model better?). Thanks in advance!
I usually set a start date and define it to be equal with the simulation time (Environment.now) 0. Since SimPy’s simulation time has no inherent unit, I also define that it is in seconds. Using arrow, I can than easily calculate an actual date and time from the current simulation time:
import arrow
import simpy
start = arrow.get('2015-01-01T00:00:00')
env = simpy.Environment()
# do some simulation ...
current_date = start.replace(seconds=env.now)
print('Curret weekday:', current_date.weekday())
You might use the datetime module and create a day_of_week object, though you would still need to calculate the elapsed time:
import datetime
# yyyy = four digit year integer
# mm = 1- or 2-digit month integer
# dd = 1- or 2-digit day integer
day_of_week = datetime.datetime(yyyy, mm, dd).strftime('%a')
if day_of_week == 'Mon':
# Do Monday tasks...
elif day_of_week == 'Tue':
# Tuesday...

How can I make pandas treat the start of the next business day as the next time after the previous business day?

I have financial trade data (timestamped with the trade time, so there are duplicate times and the datetimes are irregularly spaced). Basically I have just a datetime column and a price column in a pandas dataframe, and I've calculated returns, but I want to linearly interpolate the data so that I can get an estimate of prices every second, minute, day, etc...
It seems the best way to do this is treat the beginning of a Tuesday as occurring just after the end of Monday, so essentially modding out by the time between days. Does pandas provide an easy way to do this? I've searched the documentation and found BDay, but that doesn't seem to do what I want.
Edit: Here's a sample of my code:
df = read_csv(filePath,usecols=[0,4]) #column 0 is date_time and column 4 is price
df.date_time = pd.to_datetime(df.date_time,format = '%m-%d-%Y %H:%M:%S.%f')
def get_returns(df):
return np.log(df.Price.shift(1) / df.Price)
But my issue is that this is trade data, so that I have every trade that occurs for a given stock over some time period, trading happens only during a trading day (9:30 am - 4 pm), and the data is timestamped. I can take the price that every trade happens at and make a price series, but when I calculate kurtosis and other stylized facts, I'm getting very strange results because these sorts of statistics are usually run on evenly spaced time series data.
What I started to do was write code to interpolate my data linearly so that I could get the price every 10 seconds, minute, 10 minutes, hour, day, etc. However, with business days, weekends, holidays, and all the time where trading can't happen, I want to make python think that the only time which exists is during a business day, so that my real world times still match up with the correct date times, but not such that I need a price stamp for all the times when trading is closed.
def lin_int_tseries(series, timeChange):
tDelta = datetime.timedelta(seconds=timeChange)
data_times = series['date_time']
new_series = []
sample_times = []
sample_times.append(data_times[0])
while max(sample_times) < max(data_times):
sample_times.append(sample_times[-1] + tDelta)
for position,time in enumerate(sample_times):
try:
ind = data_times.index(time)
new_series.append(series[ind])
except:
t_next = getnextTime(time,data_times) #get next largest timestamp in data
t_prev = getprevTime(time,data_times) #get next smallest timestamp in data
ind_next = data_times.index(t_next) #index of next largest timestamp
ind_prev = data_times.index(t_prev) #index of next smallest timestamp
p_next = series[ind_next][1] #price at next timestamp
p_prev = series[ind_prev][1] #price a prev timestamp
omega = (float(time) - t_prev)/(t_next - t_prev) #linear interpolation
p_interp = (1 - omega)*p_prev + omega*p_next
new_series.append([time,p_interp])
return new_series
Sorry if it's still unclear. I just want to find some way to stitch the end of one trading day to the beginning of the next trading day, while not losing the actual datetime information.
You should use pandas resample:
df=df.resample("D")

Categories

Resources