Compare commits
2 Commits
dea1cfe889
...
7afdd39af7
Author | SHA1 | Date | |
---|---|---|---|
7afdd39af7 | |||
33d8e26c9b |
@ -53,6 +53,7 @@ INSTALLED_APPS = [
|
|||||||
# "two_factor.plugins.yubikey",
|
# "two_factor.plugins.yubikey",
|
||||||
# "otp_yubikey",
|
# "otp_yubikey",
|
||||||
"mixins",
|
"mixins",
|
||||||
|
"cachalot",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -202,6 +203,7 @@ DEBUG_TOOLBAR_PANELS = [
|
|||||||
"debug_toolbar.panels.logging.LoggingPanel",
|
"debug_toolbar.panels.logging.LoggingPanel",
|
||||||
"debug_toolbar.panels.redirects.RedirectsPanel",
|
"debug_toolbar.panels.redirects.RedirectsPanel",
|
||||||
"debug_toolbar.panels.profiling.ProfilingPanel",
|
"debug_toolbar.panels.profiling.ProfilingPanel",
|
||||||
|
"cachalot.panels.CachalotPanel",
|
||||||
]
|
]
|
||||||
|
|
||||||
from app.local_settings import * # noqa
|
from app.local_settings import * # noqa
|
||||||
|
@ -333,7 +333,7 @@ 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)
|
pairs_parsed = forms.BooleanField(widget=forms.HiddenInput, required=False)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
cleaned_data = super(AssetRestrictionForm, self).clean()
|
cleaned_data = super(AssetRestrictionForm, self).clean()
|
||||||
@ -351,5 +351,7 @@ class AssetRestrictionForm(RestrictedFormMixin, ModelForm):
|
|||||||
return
|
return
|
||||||
|
|
||||||
cleaned_data["pairs_parsed"] = new_pairs
|
cleaned_data["pairs_parsed"] = new_pairs
|
||||||
|
else:
|
||||||
|
cleaned_data["pairs_parsed"] = {}
|
||||||
|
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
@ -418,23 +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 f"{self.name} ({self.restrictions})"
|
return f"{self.name}"
|
||||||
|
|
||||||
@property
|
|
||||||
def matches(self):
|
|
||||||
"""
|
|
||||||
Get the total number of matches for this group.
|
|
||||||
"""
|
|
||||||
if isinstance(self.allowed, dict):
|
|
||||||
truthy_values = [x for x in self.allowed.values() if x is True]
|
|
||||||
return f"{len(truthy_values)}/{len(self.allowed)}"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def restrictions(self):
|
|
||||||
"""
|
|
||||||
Get the total number of restrictions for this group.
|
|
||||||
"""
|
|
||||||
return self.assetrestriction_set.count()
|
|
||||||
|
|
||||||
|
|
||||||
class AssetRestriction(models.Model):
|
class AssetRestriction(models.Model):
|
||||||
@ -442,7 +426,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, default=dict)
|
pairs_parsed = models.JSONField(null=True, blank=True, default=list)
|
||||||
|
|
||||||
webhook_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
webhook_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.Account' as last %}
|
||||||
{% include 'mixins/partials/notify.html' %}
|
{% include 'mixins/partials/notify.html' %}
|
||||||
{% cache 600 objects_accounts request.user.id object_list %}
|
{% cache 600 objects_accounts request.user.id object_list last %}
|
||||||
<table
|
<table
|
||||||
class="table is-fullwidth is-hoverable"
|
class="table is-fullwidth is-hoverable"
|
||||||
hx-target="#{{ context_object_name }}-table"
|
hx-target="#{{ context_object_name }}-table"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.AssetGroup' as last %}
|
||||||
{% include 'mixins/partials/notify.html' %}
|
{% include 'mixins/partials/notify.html' %}
|
||||||
{% cache 600 objects_assetgroups request.user.id object_list %}
|
{% cache 600 objects_assetgroups request.user.id object_list last %}
|
||||||
<table
|
<table
|
||||||
class="table is-fullwidth is-hoverable"
|
class="table is-fullwidth is-hoverable"
|
||||||
hx-target="#{{ context_object_name }}-table"
|
hx-target="#{{ context_object_name }}-table"
|
||||||
@ -14,8 +16,6 @@
|
|||||||
<th>name</th>
|
<th>name</th>
|
||||||
<th>description</th>
|
<th>description</th>
|
||||||
<th>account</th>
|
<th>account</th>
|
||||||
<th>status</th>
|
|
||||||
<th>restrictions</th>
|
|
||||||
<th>actions</th>
|
<th>actions</th>
|
||||||
</thead>
|
</thead>
|
||||||
{% for item in object_list %}
|
{% for item in object_list %}
|
||||||
@ -25,8 +25,6 @@
|
|||||||
<td>{{ item.name }}</td>
|
<td>{{ item.name }}</td>
|
||||||
<td>{{ item.description }}</td>
|
<td>{{ item.description }}</td>
|
||||||
<td>{{ item.account }}</td>
|
<td>{{ item.account }}</td>
|
||||||
<td>{{ item.matches }}</td>
|
|
||||||
<td>{{ item.restrictions }}</td>
|
|
||||||
<td>
|
<td>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<button
|
<button
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.AssetRestriction' as last %}
|
||||||
{% include 'mixins/partials/notify.html' %}
|
{% include 'mixins/partials/notify.html' %}
|
||||||
{% cache 600 objects_assetrestrictions request.user.id object_list %}
|
{% cache 600 objects_assetrestrictions request.user.id object_list last %}
|
||||||
<table
|
<table
|
||||||
class="table is-fullwidth is-hoverable"
|
class="table is-fullwidth is-hoverable"
|
||||||
hx-target="#{{ context_object_name }}-table"
|
hx-target="#{{ context_object_name }}-table"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.Callback' as last %}
|
||||||
{% include 'mixins/partials/notify.html' %}
|
{% include 'mixins/partials/notify.html' %}
|
||||||
{% cache 600 objects_callbacks request.user.id object_list %}
|
{% cache 600 objects_callbacks request.user.id object_list last %}
|
||||||
<table class="table is-fullwidth is-hoverable" id="callbacks-table">
|
<table class="table is-fullwidth is-hoverable" id="callbacks-table">
|
||||||
<thead>
|
<thead>
|
||||||
<th>id</th>
|
<th>id</th>
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.Hook' as last %}
|
||||||
{% include 'mixins/partials/notify.html' %}
|
{% include 'mixins/partials/notify.html' %}
|
||||||
{% cache 600 objects_hooks request.user.id object_list %}
|
{% cache 600 objects_hooks request.user.id object_list last %}
|
||||||
<table
|
<table
|
||||||
class="table is-fullwidth is-hoverable"
|
class="table is-fullwidth is-hoverable"
|
||||||
hx-target="#{{ context_object_name }}-table"
|
hx-target="#{{ context_object_name }}-table"
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
{% cache 600 objects_plans request.user.id plans %}
|
{% get_last_invalidation 'core.Plan' as last %}
|
||||||
|
{% cache 600 objects_plans request.user.id plans last %}
|
||||||
|
|
||||||
{% for plan in plans %}
|
{% for plan in plans %}
|
||||||
<div class="box">
|
<div class="box">
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.RiskModel' as last %}
|
||||||
{% include 'mixins/partials/notify.html' %}
|
{% include 'mixins/partials/notify.html' %}
|
||||||
{% cache 600 objects_risk request.user.id object_list %}
|
{% cache 600 objects_risk request.user.id object_list last %}
|
||||||
<table
|
<table
|
||||||
class="table is-fullwidth is-hoverable"
|
class="table is-fullwidth is-hoverable"
|
||||||
hx-target="#{{ context_object_name }}-table"
|
hx-target="#{{ context_object_name }}-table"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.Signal' as last %}
|
||||||
{% include 'mixins/partials/notify.html' %}
|
{% include 'mixins/partials/notify.html' %}
|
||||||
{% cache 600 objects_signals request.user.id object_list %}
|
{% cache 600 objects_signals request.user.id object_list last %}
|
||||||
<table
|
<table
|
||||||
class="table is-fullwidth is-hoverable"
|
class="table is-fullwidth is-hoverable"
|
||||||
hx-target="#{{ context_object_name }}-table"
|
hx-target="#{{ context_object_name }}-table"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.Strategy' as last %}
|
||||||
{% include 'mixins/partials/notify.html' %}
|
{% include 'mixins/partials/notify.html' %}
|
||||||
{% cache 600 objects_strategies request.user.id object_list %}
|
{% cache 600 objects_strategies request.user.id object_list last %}
|
||||||
<table
|
<table
|
||||||
class="table is-fullwidth is-hoverable"
|
class="table is-fullwidth is-hoverable"
|
||||||
hx-target="#{{ context_object_name }}-table"
|
hx-target="#{{ context_object_name }}-table"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.Trade' as last %}
|
||||||
{% include 'mixins/partials/notify.html' %}
|
{% include 'mixins/partials/notify.html' %}
|
||||||
{% cache 600 objects_trades request.user.id object_list %}
|
{% cache 600 objects_trades request.user.id object_list last %}
|
||||||
<table
|
<table
|
||||||
class="table is-fullwidth is-hoverable"
|
class="table is-fullwidth is-hoverable"
|
||||||
hx-target="#{{ context_object_name }}-table"
|
hx-target="#{{ context_object_name }}-table"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.TradingTime' as last %}
|
||||||
{% include 'mixins/partials/notify.html' %}
|
{% include 'mixins/partials/notify.html' %}
|
||||||
{% cache 600 objects_tradingtimes request.user.id object_list %}
|
{% cache 600 objects_tradingtimes request.user.id object_list last %}
|
||||||
<table
|
<table
|
||||||
class="table is-fullwidth is-hoverable"
|
class="table is-fullwidth is-hoverable"
|
||||||
hx-target="#{{ context_object_name }}-table"
|
hx-target="#{{ context_object_name }}-table"
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
{% load cache %}
|
{% load cache %}
|
||||||
|
{% load cachalot cache %}
|
||||||
|
{% get_last_invalidation 'core.Strategy' as last %}
|
||||||
{% include 'mixins/partials/notify.html' %}
|
{% include 'mixins/partials/notify.html' %}
|
||||||
{% cache 600 objects_trenddirections request.user.id object_list %}
|
{% cache 600 objects_trenddirections request.user.id object_list last %}
|
||||||
<table
|
<table
|
||||||
class="table is-fullwidth is-hoverable"
|
class="table is-fullwidth is-hoverable"
|
||||||
hx-target="#{{ context_object_name }}-table"
|
hx-target="#{{ context_object_name }}-table"
|
||||||
|
@ -3,7 +3,7 @@ from django.test import TestCase
|
|||||||
from core.trading.crossfilter import check_conflicting_position, check_existing_position
|
from core.trading.crossfilter import check_conflicting_position, check_existing_position
|
||||||
|
|
||||||
|
|
||||||
class MarketTestCase(TestCase):
|
class CrossfilterTestCase(TestCase):
|
||||||
def test_conflict_position(self):
|
def test_conflict_position(self):
|
||||||
position = {
|
position = {
|
||||||
"symbol": "EUR_USD",
|
"symbol": "EUR_USD",
|
7
core/trading/assetfilter.py
Normal file
7
core/trading/assetfilter.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
def get_allowed(strategy, symbol, direction):
|
||||||
|
"""
|
||||||
|
Determine whether the trade is allowed according to the Asset Groups
|
||||||
|
linked to the strategy.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# asset_group = strategy.
|
@ -5,6 +5,7 @@ from core.exchanges import common
|
|||||||
from core.exchanges.convert import get_price, side_to_direction
|
from core.exchanges.convert import get_price, side_to_direction
|
||||||
from core.lib.notify import sendmsg
|
from core.lib.notify import sendmsg
|
||||||
from core.models import Account, Strategy, Trade
|
from core.models import Account, Strategy, Trade
|
||||||
|
from core.trading import assetfilter
|
||||||
from core.trading.crossfilter import crossfilter
|
from core.trading.crossfilter import crossfilter
|
||||||
from core.trading.risk import check_risk
|
from core.trading.risk import check_risk
|
||||||
from core.util import logs
|
from core.util import logs
|
||||||
@ -325,6 +326,8 @@ def execute_strategy(callback, strategy, func):
|
|||||||
price_bound = round(price_bound, display_precision)
|
price_bound = round(price_bound, display_precision)
|
||||||
|
|
||||||
# Callback now verified
|
# Callback now verified
|
||||||
|
if func == "entry":
|
||||||
|
allowed = assetfilter.get_allowed(strategy, symbol, direction)
|
||||||
if func == "exit":
|
if func == "exit":
|
||||||
check_exit = crossfilter(account, symbol, direction, func)
|
check_exit = crossfilter(account, symbol, direction, func)
|
||||||
if check_exit is None:
|
if check_exit is None:
|
||||||
|
@ -86,6 +86,10 @@ class AssetRestrictionCreate(
|
|||||||
submit_url_name = "assetrestriction_create"
|
submit_url_name = "assetrestriction_create"
|
||||||
submit_url_args = ["type", "group"]
|
submit_url_args = ["type", "group"]
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
"""If the form is invalid, render the invalid form."""
|
||||||
|
return self.get(self.request, **self.kwargs, form=form)
|
||||||
|
|
||||||
def pre_save_mutate(self, user, obj):
|
def pre_save_mutate(self, user, obj):
|
||||||
try:
|
try:
|
||||||
assetgroup = AssetGroup.objects.get(pk=self.kwargs["group"], user=user)
|
assetgroup = AssetGroup.objects.get(pk=self.kwargs["group"], user=user)
|
||||||
|
@ -26,3 +26,4 @@ git+https://git.zm.is/XF/django-crud-mixins
|
|||||||
# For caching
|
# For caching
|
||||||
redis
|
redis
|
||||||
hiredis
|
hiredis
|
||||||
|
django-cachalot
|
||||||
|
Loading…
Reference in New Issue
Block a user