Compare commits
4 Commits
b9dc545ebb
...
master
Author | SHA1 | Date | |
---|---|---|---|
d0aedfc21f | |||
711e24d244 | |||
84879b89d1 | |||
df31c1aa49 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -8,4 +8,5 @@
|
|||||||
# Python cache:
|
# Python cache:
|
||||||
__pycache__/
|
__pycache__/
|
||||||
# Tests:
|
# Tests:
|
||||||
.coverage
|
.coverage
|
||||||
|
*.sqlite3
|
12
src/django_project/asgi.py
Normal file
12
src/django_project/asgi.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
"""
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.asgi import get_asgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_project.settings')
|
||||||
|
|
||||||
|
application = get_asgi_application()
|
@ -31,6 +31,7 @@ ALLOWED_HOSTS = []
|
|||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
|
'django_stockfin_db',
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
|
12
src/django_project/wsgi.py
Normal file
12
src/django_project/wsgi.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
"""
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_project.settings')
|
||||||
|
|
||||||
|
application = get_wsgi_application()
|
@ -1,3 +1,32 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
# Register your models here.
|
from django_stockfin_db.models import FinancialPosition, ReportingPeriod, Company, CompanyName, Figure
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(FinancialPosition)
|
||||||
|
class FinancialPositionAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('name', 'financial_statement', 'parent_name')
|
||||||
|
|
||||||
|
@admin.display(description='Parent Position Name')
|
||||||
|
def parent_name(self, obj: FinancialPosition) -> str:
|
||||||
|
return obj.parent.name if obj.parent else '-'
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(ReportingPeriod)
|
||||||
|
class ReportingPeriodAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('__str__', 'date_end', 'date_start')
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Company)
|
||||||
|
class CompanyAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('__str__', 'symbol')
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(CompanyName)
|
||||||
|
class CompanyNameAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Figure)
|
||||||
|
class FigureAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('position', 'period', 'company', 'value')
|
||||||
|
@ -1,8 +1,32 @@
|
|||||||
from django.db.models import Model, CharField, ForeignKey, TextChoices, PROTECT
|
from datetime import date
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from django.db.models import Model
|
||||||
|
from django.db.models.fields import CharField, FloatField, BooleanField, DateField, DateTimeField
|
||||||
|
from django.db.models.fields.related import ForeignKey
|
||||||
|
from django.db.models.deletion import PROTECT, CASCADE
|
||||||
|
from django.db.models.enums import TextChoices
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class FinancialPosition(Model):
|
class AbstractBaseModel(Model):
|
||||||
|
|
||||||
|
date_created = DateTimeField(
|
||||||
|
auto_now_add=True,
|
||||||
|
verbose_name=_("Time of creation")
|
||||||
|
)
|
||||||
|
date_modified = DateTimeField(
|
||||||
|
auto_now=True,
|
||||||
|
verbose_name=_("Time of last modification")
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
class FinancialPosition(AbstractBaseModel):
|
||||||
|
|
||||||
class FinStmt(TextChoices):
|
class FinStmt(TextChoices):
|
||||||
BS = 'BS', _("Balance Sheet")
|
BS = 'BS', _("Balance Sheet")
|
||||||
@ -27,3 +51,113 @@ class FinancialPosition(Model):
|
|||||||
blank=True,
|
blank=True,
|
||||||
verbose_name=_("Parent position")
|
verbose_name=_("Parent position")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"{self.financial_statement} - {self.name}"
|
||||||
|
|
||||||
|
|
||||||
|
class ReportingPeriod(AbstractBaseModel):
|
||||||
|
|
||||||
|
date_end = DateField(
|
||||||
|
db_index=True,
|
||||||
|
verbose_name=_("Period end date")
|
||||||
|
)
|
||||||
|
date_start = DateField(
|
||||||
|
db_index=True,
|
||||||
|
verbose_name=_("Period start date")
|
||||||
|
)
|
||||||
|
verified = BooleanField(
|
||||||
|
default=False,
|
||||||
|
verbose_name=_("Exact dates verified")
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
date_fmt = '%b %Y'
|
||||||
|
assert isinstance(self.date_end, date) and isinstance(self.date_start, date)
|
||||||
|
return f"{'Quarter' if self.is_quarter else 'Year'}: " \
|
||||||
|
f"{self.date_start.strftime(date_fmt)} - {self.date_end.strftime(date_fmt)}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_quarter(self) -> bool:
|
||||||
|
assert isinstance(self.date_end, date) and isinstance(self.date_start, date)
|
||||||
|
delta = self.date_end - self.date_start
|
||||||
|
return delta.days < 100
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['-date_end', 'date_start']
|
||||||
|
|
||||||
|
|
||||||
|
class Company(AbstractBaseModel):
|
||||||
|
|
||||||
|
symbol = CharField(
|
||||||
|
max_length=16,
|
||||||
|
unique=True,
|
||||||
|
verbose_name=_("Stock ticker symbol")
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"{self.name if self.name else '[~~~ NO NAME ~~~]'} ({self.symbol})"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> Optional[str]:
|
||||||
|
if self.current_name_obj is not None:
|
||||||
|
return self.current_name_obj.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_name_obj(self) -> Optional['CompanyName']:
|
||||||
|
try:
|
||||||
|
return self.company_names.latest()
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
return None
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name_plural = _("Companies")
|
||||||
|
|
||||||
|
|
||||||
|
class CompanyName(AbstractBaseModel):
|
||||||
|
|
||||||
|
company = ForeignKey(
|
||||||
|
to=Company,
|
||||||
|
on_delete=CASCADE,
|
||||||
|
related_name='company_names',
|
||||||
|
related_query_name='company_name',
|
||||||
|
)
|
||||||
|
name = CharField(
|
||||||
|
max_length=1024,
|
||||||
|
db_index=True,
|
||||||
|
verbose_name=_("Name")
|
||||||
|
)
|
||||||
|
name_since = DateField(
|
||||||
|
db_index=True,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_("Has this name since")
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
get_latest_by = ['name_since', 'date_modified']
|
||||||
|
|
||||||
|
|
||||||
|
class Figure(AbstractBaseModel):
|
||||||
|
|
||||||
|
position = ForeignKey(
|
||||||
|
to=FinancialPosition,
|
||||||
|
on_delete=PROTECT
|
||||||
|
)
|
||||||
|
period = ForeignKey(
|
||||||
|
to=ReportingPeriod,
|
||||||
|
on_delete=PROTECT
|
||||||
|
)
|
||||||
|
company = ForeignKey(
|
||||||
|
to=Company,
|
||||||
|
on_delete=PROTECT
|
||||||
|
)
|
||||||
|
value = FloatField(
|
||||||
|
verbose_name=_("Figure on financial statement")
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['company', 'period', 'position']
|
||||||
|
0
src/mw_stockfin/__init__.py
Normal file
0
src/mw_stockfin/__init__.py
Normal file
6
src/mw_stockfin/apps.py
Normal file
6
src/mw_stockfin/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class MwStockfinConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'mw_stockfin'
|
33
src/mw_stockfin/functions.py
Normal file
33
src/mw_stockfin/functions.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
from django_stockfin_db.models import FinancialPosition, ReportingPeriod, Company, Figure
|
||||||
|
|
||||||
|
from mwfin import get_balance_sheet
|
||||||
|
from mwfin.functions import ResultDict, RowData
|
||||||
|
|
||||||
|
|
||||||
|
def end_date_to_reporting_period(date_str: str) -> ReportingPeriod:
|
||||||
|
"""Convert an end date string into parameters for a ReportingPeriod, `get_or_create` the object and return it."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_create_position(name: str, parent_position: FinancialPosition = None) -> FinancialPosition:
|
||||||
|
return FinancialPosition.objects.get_or_create(name=name, parent=parent_position)
|
||||||
|
|
||||||
|
|
||||||
|
def data_row_to_figures(company: Company, position: FinancialPosition, data: RowData):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def save_single_result_dict(fin_stmt: str, symbol: str, result_dict: ResultDict):
|
||||||
|
company = Company.objects.get_or_create(symbol=symbol)
|
||||||
|
|
||||||
|
|
||||||
|
def result_dicts_to_db(fin_stmt: str, **data: ResultDict):
|
||||||
|
for symbol, result_dict in data.items():
|
||||||
|
save_single_result_dict(fin_stmt, symbol, result_dict)
|
||||||
|
|
||||||
|
|
||||||
|
async def main(*symbols: str):
|
||||||
|
data = await get_balance_sheet(*symbols)
|
||||||
|
if len(symbols) == 1:
|
||||||
|
save_single_result_dict('BS', symbols[0], data)
|
||||||
|
result_dicts_to_db('BS', **data)
|
Reference in New Issue
Block a user