You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
7.1 KiB
Python

from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import IntegrityError
from django.http import HttpResponse
from mixins.views import AbortSave, ObjectCreate, ObjectDelete, ObjectList, ObjectUpdate
from rest_framework import status
from rest_framework.parsers import JSONParser
from rest_framework.views import APIView
from core.forms import AssetGroupForm, AssetRuleForm
from core.models import AssetGroup, AssetRule
from core.trading import assetfilter
from core.util import logs
log = logs.get_logger(__name__)
# Asset Groups
class AssetGroupList(LoginRequiredMixin, ObjectList):
list_template = "partials/assetgroup-list.html"
model = AssetGroup
page_title = "List of asset groups for aggregations. Linked to strategies."
page_subtitle = "Asset groups are maps between currencies and their sentiment."
list_url_name = "assetgroups"
list_url_args = ["type"]
submit_url_name = "assetgroup_create"
class AssetGroupCreate(LoginRequiredMixin, ObjectCreate):
model = AssetGroup
form_class = AssetGroupForm
submit_url_name = "assetgroup_create"
class AssetGroupUpdate(LoginRequiredMixin, ObjectUpdate):
model = AssetGroup
form_class = AssetGroupForm
submit_url_name = "assetgroup_update"
class AssetGroupDelete(LoginRequiredMixin, ObjectDelete):
model = AssetGroup
# Asset Rules
class AssetRulesPermissionMixin:
# Check the user has permission to view the asset group
# We have a user check on the AssetRestriction, but we need to check the
# AssetGroup as well
def set_extra_args(self, user):
self.extra_permission_args = {
"group__user": user,
"group__pk": self.kwargs["group"],
}
class AssetRuleList(LoginRequiredMixin, AssetRulesPermissionMixin, ObjectList):
list_template = "partials/assetrule-list.html"
model = AssetRule
page_title = "List of asset rules"
list_url_name = "assetrules"
list_url_args = ["type", "group"]
submit_url_name = "assetrule_create"
submit_url_args = ["type", "group"]
class AssetRuleCreate(LoginRequiredMixin, AssetRulesPermissionMixin, ObjectCreate):
model = AssetRule
form_class = AssetRuleForm
submit_url_name = "assetrule_create"
submit_url_args = ["type", "group"]
def form_valid(self, form):
"""If the form is invalid, render the invalid form."""
try:
return super().form_valid(form)
except IntegrityError as e:
if "UNIQUE constraint failed" in str(e):
form.add_error("asset", "Asset rule already exists")
return self.form_invalid(form)
else:
raise e
def pre_save_mutate(self, user, obj):
try:
assetgroup = AssetGroup.objects.get(pk=self.kwargs["group"], user=user)
obj.group = assetgroup
except AssetGroup.DoesNotExist:
log.error(f"Asset Group {self.kwargs['group']} does not exist")
raise AbortSave("asset group does not exist or you don't have access")
class AssetRuleUpdate(LoginRequiredMixin, AssetRulesPermissionMixin, ObjectUpdate):
model = AssetRule
form_class = AssetRuleForm
submit_url_name = "assetrule_update"
submit_url_args = ["type", "pk", "group"]
class AssetRuleDelete(LoginRequiredMixin, AssetRulesPermissionMixin, ObjectDelete):
model = AssetRule
class AssetGroupAPI(APIView):
parser_classes = [JSONParser]
def post(self, request, webhook_id):
# log.debug(f"AssetAPI POST {webhook_id}: {request.data}")
# print(json.dumps(request.data, indent=2))
try:
group = AssetGroup.objects.get(webhook_id=webhook_id)
except AssetGroup.DoesNotExist:
log.error(f"Asset group {webhook_id} does not exist")
return HttpResponse(status=status.HTTP_404_NOT_FOUND)
# if restriction.group is not None:
# group = restriction.group
# else:
# log.error(f"Asset restriction {restriction} has no group")
# return HttpResponse(status=status.HTTP_404_NOT_FOUND)
# if group.strategy_set.exists() is not None:
# strategies = group.strategy_set.all()
# else:
# log.error(f"Asset group {group} has no strategy")
# return HttpResponse(status=status.HTTP_404_NOT_FOUND)
# log.debug(f"Asset API {webhook_id} matched to strategies {strategies}")
if "meta" not in request.data:
log.error(f"Asset API {webhook_id} has no meta")
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
if "is_match" not in request.data["meta"]:
log.error(f"Asset API {webhook_id} has no is_match")
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
is_match = request.data["meta"]["is_match"]
if "topic" not in request.data:
log.error(f"Asset API {webhook_id} has no topic")
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
topic = request.data["topic"]
new_assets = []
assets_split = topic.split(",")
if not assets_split:
log.error(f"Asset API {webhook_id} topic {topic} is not an asset")
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
for asset in assets_split:
if asset:
new_assets.append(asset.strip())
else:
log.error(f"Asset API {webhook_id} asset {asset} is not an asset")
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
aggregation = "none"
value = None
# Check if we have lower/upper bounds
if "aggs" in request.data["meta"]:
aggs = request.data["meta"]["aggs"]
# TODO: support more aggregations
if len(aggs) != 1:
log.error(
f"Asset API {webhook_id} has an invalid number of aggregations"
)
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
first_agg_key = list(aggs.keys())[0]
if "value" in aggs[first_agg_key]:
aggregation = first_agg_key
value = aggs[first_agg_key]["value"]
for asset in new_assets:
asset_rule, _ = AssetRule.objects.get_or_create(
group=group, user=group.user, asset=asset
)
if is_match:
# Make our own is_match based on the aggregations
is_match = assetfilter.check_asset_aggregation(
value, asset_rule.trigger_above, asset_rule.trigger_below
)
else:
# No matches
is_match = None
asset_rule.aggregation = aggregation
if value:
asset_rule.value = value
if is_match is not None:
our_status = is_match
else:
our_status = 1
asset_rule.original_status = our_status
asset_rule.status = assetfilter.update_status_from_mappings(
our_status, group
)
asset_rule.save()
return HttpResponse(status=status.HTTP_200_OK)