2023-02-10 07:20:19 +00:00
|
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
2023-02-13 20:45:23 +00:00
|
|
|
from django.db import IntegrityError
|
2023-02-10 22:04:01 +00:00
|
|
|
from django.http import HttpResponse
|
2023-02-13 20:45:23 +00:00
|
|
|
from mixins.views import AbortSave, ObjectCreate, ObjectDelete, ObjectList, ObjectUpdate
|
2023-02-10 22:04:01 +00:00
|
|
|
from rest_framework import status
|
|
|
|
from rest_framework.parsers import JSONParser
|
|
|
|
from rest_framework.views import APIView
|
2023-02-10 07:20:19 +00:00
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
from core.forms import AssetGroupForm, AssetRuleForm
|
|
|
|
from core.models import AssetGroup, AssetRule
|
2023-02-13 07:20:40 +00:00
|
|
|
from core.trading import assetfilter
|
2023-02-10 07:20:19 +00:00
|
|
|
from core.util import logs
|
|
|
|
|
|
|
|
log = logs.get_logger(__name__)
|
|
|
|
|
2023-02-10 14:33:17 +00:00
|
|
|
# Asset Groups
|
|
|
|
|
2023-02-10 07:20:19 +00:00
|
|
|
|
|
|
|
class AssetGroupList(LoginRequiredMixin, ObjectList):
|
|
|
|
list_template = "partials/assetgroup-list.html"
|
|
|
|
model = AssetGroup
|
2023-02-13 17:50:46 +00:00
|
|
|
page_title = "List of asset groups for aggregations. Linked to strategies."
|
|
|
|
page_subtitle = "Asset groups are maps between currencies and their sentiment."
|
2023-02-10 07:20:19 +00:00
|
|
|
|
|
|
|
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
|
2023-02-10 14:33:17 +00:00
|
|
|
|
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
# Asset Rules
|
2023-02-10 14:33:17 +00:00
|
|
|
|
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
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"],
|
|
|
|
}
|
2023-02-10 14:33:17 +00:00
|
|
|
|
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
class AssetRuleList(LoginRequiredMixin, AssetRulesPermissionMixin, ObjectList):
|
|
|
|
list_template = "partials/assetrule-list.html"
|
|
|
|
model = AssetRule
|
|
|
|
page_title = "List of asset rules"
|
2023-02-10 14:33:17 +00:00
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
list_url_name = "assetrules"
|
|
|
|
list_url_args = ["type", "group"]
|
2023-02-10 14:33:17 +00:00
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
submit_url_name = "assetrule_create"
|
|
|
|
submit_url_args = ["type", "group"]
|
2023-02-10 14:33:17 +00:00
|
|
|
|
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
class AssetRuleCreate(LoginRequiredMixin, AssetRulesPermissionMixin, ObjectCreate):
|
|
|
|
model = AssetRule
|
|
|
|
form_class = AssetRuleForm
|
2023-02-10 14:33:17 +00:00
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
submit_url_name = "assetrule_create"
|
|
|
|
submit_url_args = ["type", "group"]
|
2023-02-10 14:33:17 +00:00
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
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
|
2023-02-11 17:45:22 +00:00
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
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")
|
2023-02-10 14:33:17 +00:00
|
|
|
|
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
class AssetRuleUpdate(LoginRequiredMixin, AssetRulesPermissionMixin, ObjectUpdate):
|
|
|
|
model = AssetRule
|
|
|
|
form_class = AssetRuleForm
|
2023-02-10 14:33:17 +00:00
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
submit_url_name = "assetrule_update"
|
|
|
|
submit_url_args = ["type", "pk", "group"]
|
2023-02-10 14:33:17 +00:00
|
|
|
|
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
class AssetRuleDelete(LoginRequiredMixin, AssetRulesPermissionMixin, ObjectDelete):
|
|
|
|
model = AssetRule
|
2023-02-10 22:04:01 +00:00
|
|
|
|
|
|
|
|
2023-02-13 07:20:40 +00:00
|
|
|
class AssetGroupAPI(APIView):
|
2023-02-10 22:04:01 +00:00
|
|
|
parser_classes = [JSONParser]
|
|
|
|
|
|
|
|
def post(self, request, webhook_id):
|
2023-02-10 23:26:30 +00:00
|
|
|
# log.debug(f"AssetAPI POST {webhook_id}: {request.data}")
|
2023-02-14 07:20:47 +00:00
|
|
|
# print(json.dumps(request.data, indent=2))
|
2023-02-10 22:04:01 +00:00
|
|
|
|
|
|
|
try:
|
2023-02-13 07:20:40 +00:00
|
|
|
group = AssetGroup.objects.get(webhook_id=webhook_id)
|
|
|
|
except AssetGroup.DoesNotExist:
|
|
|
|
log.error(f"Asset group {webhook_id} does not exist")
|
2023-02-10 22:04:01 +00:00
|
|
|
return HttpResponse(status=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
2023-02-13 07:20:40 +00:00
|
|
|
# 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)
|
2023-02-10 23:26:30 +00:00
|
|
|
|
|
|
|
# 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}")
|
|
|
|
|
2023-02-13 07:20:40 +00:00
|
|
|
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"]
|
2023-02-13 17:50:46 +00:00
|
|
|
new_assets = []
|
|
|
|
assets_split = topic.split(",")
|
|
|
|
if not assets_split:
|
|
|
|
log.error(f"Asset API {webhook_id} topic {topic} is not an asset")
|
2023-02-13 07:20:40 +00:00
|
|
|
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
|
2023-02-13 17:50:46 +00:00
|
|
|
for asset in assets_split:
|
|
|
|
if asset:
|
|
|
|
new_assets.append(asset.strip())
|
2023-02-13 07:20:40 +00:00
|
|
|
else:
|
2023-02-13 17:50:46 +00:00
|
|
|
log.error(f"Asset API {webhook_id} asset {asset} is not an asset")
|
2023-02-13 07:20:40 +00:00
|
|
|
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
2023-02-13 20:45:23 +00:00
|
|
|
aggregation = "none"
|
|
|
|
value = None
|
2023-02-13 07:20:40 +00:00
|
|
|
# Check if we have lower/upper bounds
|
2023-02-13 20:45:23 +00:00
|
|
|
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"]
|
2023-02-13 07:20:40 +00:00
|
|
|
|
2023-02-13 17:50:46 +00:00
|
|
|
for asset in new_assets:
|
2023-02-13 20:45:23 +00:00
|
|
|
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()
|
2023-02-13 07:20:40 +00:00
|
|
|
|
|
|
|
return HttpResponse(status=status.HTTP_200_OK)
|