Implement storing asset restriction callbacks

This commit is contained in:
Mark Veidemanis 2023-02-10 23:26:30 +00:00
parent c283c6c192
commit 010aba7f81
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
5 changed files with 78 additions and 8 deletions

View File

@ -89,10 +89,10 @@ class AccountForm(RestrictedFormMixin, ModelForm):
"exchange", "exchange",
"api_key", "api_key",
"api_secret", "api_secret",
"sandbox",
"enabled",
"risk_model", "risk_model",
"initial_balance", "initial_balance",
"sandbox",
"enabled",
) )
help_texts = { help_texts = {
"name": "Name of the account. Informational only.", "name": "Name of the account. Informational only.",
@ -119,6 +119,7 @@ class StrategyForm(RestrictedFormMixin, ModelForm):
"name", "name",
"description", "description",
"account", "account",
"asset_group",
"trading_times", "trading_times",
"order_type", "order_type",
"time_in_force", "time_in_force",
@ -138,6 +139,7 @@ class StrategyForm(RestrictedFormMixin, ModelForm):
"name": "Name of the strategy. Informational only.", "name": "Name of the strategy. Informational only.",
"description": "Description of the strategy. Informational only.", "description": "Description of the strategy. Informational only.",
"account": "The account to use for this strategy.", "account": "The account to use for this strategy.",
"asset_group": "Asset groups determine which pairs can be traded.",
"trading_times": "When the strategy will place new trades.", "trading_times": "When the strategy will place new trades.",
"order_type": "Market: Buy/Sell at the current market price. Limit: Buy/Sell at a specified price. Limits protect you more against market slippage.", "order_type": "Market: Buy/Sell at the current market price. Limit: Buy/Sell at a specified price. Limits protect you more against market slippage.",
"time_in_force": "The time in force controls how the order is executed.", "time_in_force": "The time in force controls how the order is executed.",
@ -323,6 +325,7 @@ class AssetRestrictionForm(RestrictedFormMixin, ModelForm):
"name", "name",
"description", "description",
"pairs", "pairs",
"pairs_parsed",
) )
help_texts = { help_texts = {
"name": "Name of the asset restriction group. Informational only.", "name": "Name of the asset restriction group. Informational only.",
@ -330,6 +333,8 @@ class AssetRestrictionForm(RestrictedFormMixin, ModelForm):
"pairs": "Comma-separated list of pairs to restrict when a webhook is received. This does nothing on its own.", "pairs": "Comma-separated list of pairs to restrict when a webhook is received. This does nothing on its own.",
} }
pairs_parsed = forms.BooleanField(widget=forms.HiddenInput)
def clean(self): def clean(self):
cleaned_data = super(AssetRestrictionForm, self).clean() cleaned_data = super(AssetRestrictionForm, self).clean()
if "pairs" in cleaned_data and cleaned_data["pairs"]: if "pairs" in cleaned_data and cleaned_data["pairs"]:

View File

@ -0,0 +1,19 @@
# Generated by Django 4.1.6 on 2023-02-10 22:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('core', '0054_assetrestriction_webhook_id_alter_assetgroup_allowed'),
]
operations = [
migrations.AddField(
model_name='strategy',
name='asset_group',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='core.assetgroup'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.6 on 2023-02-10 23:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0055_strategy_asset_group'),
]
operations = [
migrations.AlterField(
model_name='assetrestriction',
name='pairs_parsed',
field=models.JSONField(blank=True, default=dict, null=True),
),
]

View File

@ -369,6 +369,10 @@ class Strategy(models.Model):
trade_size_percent = models.FloatField(default=0.5) trade_size_percent = models.FloatField(default=0.5)
trends = models.JSONField(null=True, blank=True) trends = models.JSONField(null=True, blank=True)
asset_group = models.ForeignKey(
"core.AssetGroup", on_delete=models.PROTECT, null=True, blank=True
)
class Meta: class Meta:
verbose_name_plural = "strategies" verbose_name_plural = "strategies"
@ -414,7 +418,7 @@ class AssetGroup(models.Model):
allowed = models.JSONField(null=True, blank=True, default=dict) allowed = models.JSONField(null=True, blank=True, default=dict)
def __str__(self): def __str__(self):
return self.name return f"{self.name} ({self.restrictions})"
@property @property
def matches(self): def matches(self):
@ -438,7 +442,7 @@ class AssetRestriction(models.Model):
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
description = models.TextField(null=True, blank=True) description = models.TextField(null=True, blank=True)
pairs = models.CharField(max_length=4096, null=True, blank=True) pairs = models.CharField(max_length=4096, null=True, blank=True)
pairs_parsed = models.JSONField(null=True, blank=True) pairs_parsed = models.JSONField(null=True, blank=True, default=dict)
webhook_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) webhook_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)

View File

@ -8,6 +8,7 @@ from rest_framework.views import APIView
from core.forms import AssetGroupForm, AssetRestrictionForm from core.forms import AssetGroupForm, AssetRestrictionForm
from core.models import AssetGroup, AssetRestriction from core.models import AssetGroup, AssetRestriction
from core.util import logs from core.util import logs
import json
log = logs.get_logger(__name__) log = logs.get_logger(__name__)
@ -109,12 +110,35 @@ class AssetRestrictionAPI(APIView):
parser_classes = [JSONParser] parser_classes = [JSONParser]
def post(self, request, webhook_id): def post(self, request, webhook_id):
log.debug(f"AssetAPI POST {webhook_id}: {request.data}") # log.debug(f"AssetAPI POST {webhook_id}: {request.data}")
print(json.dumps(request.data, indent=2))
try: try:
webhook = AssetRestriction.objects.get(webhook_id=webhook_id) restriction = AssetRestriction.objects.get(webhook_id=webhook_id)
except AssetRestriction.DoesNotExist: except AssetRestriction.DoesNotExist:
log.error(f"AssetRestriction {webhook_id} does not exist") log.error(f"Asset restriction {webhook_id} does not exist")
return HttpResponse(status=status.HTTP_404_NOT_FOUND) 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" in request.data:
if "is_match" in request.data["meta"]:
is_match = request.data["meta"]["is_match"]
if isinstance(restriction.pairs_parsed, list):
for pair in restriction.pairs_parsed:
group.allowed[pair] = is_match
group.save()
return HttpResponse(status=status.HTTP_200_OK) return HttpResponse(status=status.HTTP_200_OK)
return HttpResponse(status=status.HTTP_400_BAD_REQUEST)