Implement trend signals

This commit is contained in:
Mark Veidemanis 2022-12-06 19:46:06 +00:00
parent 242c9fbaed
commit 3b3faecdf1
Signed by: m
GPG Key ID: 5ACFCEED46C0904F
5 changed files with 87 additions and 3 deletions

View File

@ -122,6 +122,7 @@ class StrategyForm(RestrictedFormMixin, ModelForm):
"time_in_force",
"entry_signals",
"exit_signals",
"trend_signals",
"enabled",
"take_profit_percent",
"stop_loss_percent",
@ -138,8 +139,9 @@ class StrategyForm(RestrictedFormMixin, ModelForm):
"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.",
"time_in_force": "The time in force controls how the order is executed.",
"entry_signals": "The entry signals to attach to this strategy. Callbacks received to these signals will trigger a trade.",
"exit_signals": "The exit signals to attach to this strategy. Callbacks received to these signals will close all trades for the symbol on the account.",
"entry_signals": "Callbacks received to these signals will trigger a trade.",
"exit_signals": "Callbacks received to these signals will close all trades for the symbol on the account.",
"trend_signals": "Callbacks received to these signals will limit the trading direction of the given symbol to the callback direction until further notice.",
"enabled": "Whether the strategy is enabled.",
"take_profit_percent": "The take profit will be set at this percentage above/below the entry price.",
"stop_loss_percent": "The stop loss will be set at this percentage above/below the entry price.",
@ -161,6 +163,12 @@ class StrategyForm(RestrictedFormMixin, ModelForm):
help_text=Meta.help_texts["exit_signals"],
required=False,
)
trend_signals = forms.ModelMultipleChoiceField(
queryset=Signal.objects.all(),
widget=forms.CheckboxSelectMultiple,
help_text=Meta.help_texts["trend_signals"],
required=False,
)
trading_times = forms.ModelMultipleChoiceField(
queryset=TradingTime.objects.all(), widget=forms.CheckboxSelectMultiple
)

View File

@ -296,7 +296,7 @@ def execute_strategy(callback, strategy, func):
:param strategy: Strategy object
"""
# Only check times for entries. We can always exit trades.
# Only check times for entries. We can always exit trades and set trends.
if func == "entry":
# Check if we can trade now!
now_utc = datetime.utcnow()
@ -380,6 +380,31 @@ def execute_strategy(callback, strategy, func):
response = account.client.close_position(side, symbol)
log.debug(f"Close position response: {response}")
return
# Set the trend
elif func == "trend":
if strategy.trends is None:
strategy.trends = {}
strategy.trends[symbol] = direction
strategy.save()
log.debug(f"Set trend for {symbol}: {direction}")
return
# Check if we are trading against the trend
if strategy.trend_signals is not None:
if strategy.trends is None:
log.debug("Refusing to trade with no trend signals received")
return
if symbol not in strategy.trends:
log.debug("Refusing to trade asset without established trend")
return
else:
if strategy.trends[symbol] != direction:
log.debug("Refusing to trade against the trend")
return
else:
log.debug(f"Trend check passed for {symbol} - {direction}")
type = strategy.order_type
# Get the account's balance in the native account currency
@ -443,6 +468,17 @@ def execute_strategy(callback, strategy, func):
def process_callback(callback):
log.info(f"Received callback for {callback.hook} - {callback.signal}")
# Scan for trend
log.debug("Scanning for trend strategies...")
strategies = Strategy.objects.filter(trend_signals=callback.signal, enabled=True)
log.debug(f"Matched strategies: {strategies}")
for strategy in strategies:
log.debug(f"Executing strategy {strategy}")
if callback.hook.user != strategy.user:
log.error("Ownership differs between callback and strategy.")
continue
execute_strategy(callback, strategy, func="trend")
# Scan for entry
log.debug("Scanning for entry strategies...")
strategies = Strategy.objects.filter(entry_signals=callback.signal, enabled=True)

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.3 on 2022-12-06 19:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0042_trade_information'),
]
operations = [
migrations.AddField(
model_name='strategy',
name='trend_signals',
field=models.ManyToManyField(blank=True, related_name='trend_strategies', to='core.signal'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.3 on 2022-12-06 19:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0043_strategy_trend_signals'),
]
operations = [
migrations.AddField(
model_name='strategy',
name='trends',
field=models.JSONField(blank=True, null=True),
),
]

View File

@ -334,6 +334,9 @@ class Strategy(models.Model):
exit_signals = models.ManyToManyField(
Signal, related_name="exit_strategies", blank=True
)
trend_signals = models.ManyToManyField(
Signal, related_name="trend_strategies", blank=True
)
enabled = models.BooleanField(default=False)
take_profit_percent = models.FloatField(default=1.5)
stop_loss_percent = models.FloatField(default=1.0)
@ -341,6 +344,7 @@ class Strategy(models.Model):
price_slippage_percent = models.FloatField(default=2.5)
callback_price_deviation_percent = models.FloatField(default=0.5)
trade_size_percent = models.FloatField(default=0.5)
trends = models.JSONField(null=True, blank=True)
class Meta:
verbose_name_plural = "strategies"