Skip to content

Commit

Permalink
Merge pull request #117 from napse-invest/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
tomjeannesson authored Aug 20, 2023
2 parents a0f308e + dace21b commit f84252e
Show file tree
Hide file tree
Showing 25 changed files with 271 additions and 600 deletions.
173 changes: 13 additions & 160 deletions django_napse/core/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.1.7 on 2023-08-14 16:31
# Generated by Django 4.1.7 on 2023-08-17 11:29

import datetime
from django.db import migrations, models
Expand Down Expand Up @@ -83,6 +83,17 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
("share", models.FloatField()),
("breakpoint", models.FloatField()),
("autoscale", models.BooleanField()),
(
"bot",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="cluster",
to="django_napse_core.bot",
),
),
],
),
migrations.CreateModel(
Expand Down Expand Up @@ -132,20 +143,6 @@ class Migration(migrations.Migration):
("last_settings_update", models.DateTimeField(null=True)),
],
),
migrations.CreateModel(
name="DefaultFleetOperator",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
],
),
migrations.CreateModel(
name="Exchange",
fields=[
Expand Down Expand Up @@ -405,23 +402,6 @@ class Migration(migrations.Migration):
],
bases=("django_napse_core.strategy",),
),
migrations.CreateModel(
name="EquilibriumFleetOperator",
fields=[
(
"defaultfleetoperator_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="django_napse_core.defaultfleetoperator",
),
),
],
bases=("django_napse_core.defaultfleetoperator",),
),
migrations.CreateModel(
name="LBOPlugin",
fields=[
Expand Down Expand Up @@ -473,23 +453,6 @@ class Migration(migrations.Migration):
],
bases=("django_napse_core.plugin",),
),
migrations.CreateModel(
name="SpecificSharesFleetOperator",
fields=[
(
"defaultfleetoperator_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="django_napse_core.defaultfleetoperator",
),
),
],
bases=("django_napse_core.defaultfleetoperator",),
),
migrations.CreateModel(
name="StrategyModification",
fields=[
Expand Down Expand Up @@ -712,6 +675,7 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
("importance", models.FloatField()),
(
"bot",
models.OneToOneField(
Expand Down Expand Up @@ -795,15 +759,6 @@ class Migration(migrations.Migration):
),
],
),
migrations.AddField(
model_name="defaultfleetoperator",
name="fleet",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="operator",
to="django_napse_core.fleet",
),
),
migrations.CreateModel(
name="Debit",
fields=[
Expand Down Expand Up @@ -899,108 +854,6 @@ class Migration(migrations.Migration):
to="django_napse_core.strategy",
),
),
migrations.CreateModel(
name="SpecificShare",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("share", models.FloatField()),
(
"cluster",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="specific_shares",
to="django_napse_core.cluster",
),
),
(
"operator",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="specific_shares",
to="django_napse_core.defaultfleetoperator",
),
),
],
options={
"unique_together": {("cluster", "operator")},
},
),
migrations.CreateModel(
name="SpecificBreakPoint",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("scale_up_breakpoint", models.FloatField()),
(
"cluster",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="specific_breakpoints",
to="django_napse_core.cluster",
),
),
(
"operator",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="specific_breakpoints",
to="django_napse_core.defaultfleetoperator",
),
),
],
options={
"unique_together": {("cluster", "operator")},
},
),
migrations.CreateModel(
name="SpecificAutoscale",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("autoscale", models.BooleanField(default=True)),
(
"cluster",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="specific_autoscales",
to="django_napse_core.cluster",
),
),
(
"operator",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="specific_autoscales",
to="django_napse_core.defaultfleetoperator",
),
),
],
options={
"unique_together": {("cluster", "operator")},
},
),
migrations.CreateModel(
name="SpaceWallet",
fields=[
Expand Down
16 changes: 16 additions & 0 deletions django_napse/core/models/bots/architecture.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,22 @@ def controllers_dict(self): # pragma: no cover
error_msg = f"controllers_dict not implemented for the Architecture base class, please implement it in the {self.__class__} class."
raise NotImplementedError(error_msg)

def accepted_tickers(self): # pragma: no cover
if self.__class__ == Architecture:
error_msg = "accepted_tickers not implemented for the Architecture base class, please implement it in a subclass."
else:
error_msg = f"accepted_tickers not implemented for the Architecture base class, please implement it in the {self.__class__} class."
raise NotImplementedError(error_msg)

def accepted_investment_tickers(self): # pragma: no cover
if self.__class__ == Architecture:
error_msg = "accepted_investment_tickers not implemented for the Architecture base class, please implement it in a subclass."
else:
error_msg = (
f"accepted_investment_tickers not implemented for the Architecture base class, please implement it in the {self.__class__} class."
)
raise NotImplementedError(error_msg)

def get_extras(self):
return {}

Expand Down
6 changes: 6 additions & 0 deletions django_napse/core/models/bots/architectures/single_pair.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,9 @@ def architecture_modifications(self, order: dict, data: dict):
"ignore_failed_order": True,
},
]

def accepted_tickers(self):
return [self.controller.base, self.controller.quote]

def accepted_investment_tickers(self):
return [self.controller.quote]
2 changes: 1 addition & 1 deletion django_napse/core/models/bots/plugins/mbp.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def plugin_category(cls):
def _apply(self, data: dict) -> dict:
order = data["order"]
if order["side"] == SIDES.BUY:
new_mbp = f"{order['controller'].base}|(a * b + c) / (c / {order['price']} + a)"
new_mbp = f"{order['controller'].base}|{order['price']}"
order["ConnectionModifications"] += [
{
"key": "mbp",
Expand Down
4 changes: 2 additions & 2 deletions django_napse/core/models/bots/strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,6 @@ def connect(self, connection):

def copy(self):
return self.find().__class__.objects.create(
config=self.config.duplicate_immutable(),
architecture=self.architecture.copy(),
config=self.config.find().duplicate_immutable(),
architecture=self.architecture.find().copy(),
)
4 changes: 2 additions & 2 deletions django_napse/core/models/connections/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ def info(self, verbose=True, beacon=""):

string += f"{beacon}Wallet:\n"
new_beacon = beacon + "\t"
string += f"{beacon}\t{self.wallet.info(verbose=False, beacon=new_beacon)}\n"
string += f"{self.wallet.info(verbose=False, beacon=new_beacon)}\n"

string += f"{beacon}ConnectionSpecificArgs:\n"
query = self.specific_args.all()
if query.count() == 0:
string += f"{beacon}\tNone\n"
else:
for connection_specific_arg in query:
string += f"{beacon}\t{connection_specific_arg.info(verbose=False, beacon=new_beacon)}\n"
string += f"{connection_specific_arg.info(verbose=False, beacon=new_beacon)}\n"

if verbose: # pragma: no cover
print(string)
Expand Down
83 changes: 75 additions & 8 deletions django_napse/core/models/fleets/cluster.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,91 @@
from django.db import models

from django_napse.utils.errors import ClusterError
from django_napse.core.models.connections.connection import Connection
from django_napse.core.models.fleets.link import Link
from django_napse.core.models.transactions.transaction import Transaction
from django_napse.utils.constants import TRANSACTION_TYPES
from django_napse.utils.errors import BotError, ClusterError


class Cluster(models.Model):
fleet = models.ForeignKey("Fleet", on_delete=models.CASCADE, related_name="clusters")
bot = models.OneToOneField("Bot", on_delete=models.CASCADE, related_name="cluster")
share = models.FloatField()
breakpoint = models.FloatField()
autoscale = models.BooleanField()

def __str__(self):
return f"Cluster: {self.fleet}"

def save(self, *args, **kwargs):
configs = [link.bot.strategy.config for link in self.links.all()]
if len(set(configs)) != 1:
error_msg = "All bots in a cluster must have the same config."
raise ClusterError.MultipleConfigs(error_msg)
if not configs[0].immutable:
error_msg = "The config must be immutable."
if not self.config.immutable:
error_msg = "In a fleet, the config must be immutable."
raise ClusterError.MutableBotConfig(error_msg)
return super().save(*args, **kwargs)

def info(self, verbose=True, beacon=""):
string = ""
string += f"{beacon}Cluster {self.pk}:\n"
string += f"{beacon}Args:\n"
string += f"{beacon}\t{self.fleet=}\n"
string += f"{beacon}\t{self.bot=}\n"
string += f"{beacon}\t{self.share=}\n"
string += f"{beacon}\t{self.breakpoint=}\n"
string += f"{beacon}\t{self.autoscale=}\n"

new_beacon = beacon + "\t"
string += f"{beacon}Links:\n"
for link in self.links.all():
string += f"{link.info(verbose=False, beacon=new_beacon)}\n"

string += f"{beacon}Connections:\n"
connections = []
for link in self.links.all():
connections += list(link.bot.connections.all())
for connection in connections:
string += f"{connection.info(verbose=False, beacon=new_beacon)}\n"

if verbose: # pragma: no cover
print(string)
return string

@property
def config(self):
return self.links.first().bot.strategy.config
return self.bot.strategy.config

def invest(self, space, amount, ticker):
all_connections = []
bots = [link.bot for link in self.links.all().order_by("importance")]
if len(bots) == 0:
new_bot = self.bot.copy()
Link.objects.create(bot=new_bot, cluster=self, importance=1)
connection = Connection.objects.create(bot=new_bot, owner=space.wallet)
Transaction.objects.create(
from_wallet=space.wallet,
to_wallet=connection.wallet,
amount=amount,
ticker=ticker,
transaction_type=TRANSACTION_TYPES.CONNECTION_DEPOSIT,
)
all_connections.append(connection)
elif len(bots) == 1:
bot = bots[0]
if ticker not in bot.architecture.accepted_investment_tickers() or ticker not in bot.architecture.accepted_tickers():
error_msg = f"Bot {bot} does not accept ticker {ticker}."
raise BotError.InvalidTicker(error_msg)
try:
connection = Connection.objects.get(bot=bot, owner=space.wallet)
except Connection.DoesNotExist:
connection = Connection.objects.create(bot=bot, owner=space.wallet)
Transaction.objects.create(
from_wallet=space.wallet,
to_wallet=connection.wallet,
amount=amount,
ticker=ticker,
transaction_type=TRANSACTION_TYPES.CONNECTION_DEPOSIT,
)
all_connections.append(connection)
else:
error_msg = "Autoscale not implemented yet."
raise NotImplementedError(error_msg)
return all_connections
Loading

0 comments on commit f84252e

Please sign in to comment.