diff --git a/Makefile b/Makefile
index 277af169..44d25284 100644
--- a/Makefile
+++ b/Makefile
@@ -6,11 +6,11 @@ all: setup-testing-environment makemigrations migrate runserver
setup:
ifeq ($(OS),Darwin) # Mac OS X
- ./setup-osx.sh
+ ./setup/setup-osx.sh
else ifeq ($(OS),Linux)
- ./setup-unix.sh
+ ./setup/setup-unix.sh
else
- ./setup-windows.sh
+ ./setup/setup-windows.sh
endif
setup-testing-environment:
diff --git a/README.md b/README.md
index eb2abb2e..61cc3e3b 100644
--- a/README.md
+++ b/README.md
@@ -22,47 +22,29 @@
Django Napse ยท
- Usefull commands .
+ Documentation .
How to contribute
## django-napse
-....
-## Useful commands
-Unless otherwise specified, all commands are to be run at the root folder of the project.
+Django napse is a portfolio management module using trading bots.
+This directory can be used as a django module, which you can install as follows:
-### Create a new project
-- Unix \
-```source setup-unix.sh```
-
-- Windows \
-```.\setup-windows.ps1```
+```bash
+pip install django-napse
+```
-### Run a test version of the project
+Or you can use it as a local backend for the [Napse desktop application](https://github.com/napse-invest/Napse), by cloning the repository.
-- Build migrations \
-```make makemigrations```
-- Apply migrations \
-```make migrate```
-- Run server \
-```make runserver```
+Find more details for installation in the [documentation](https://napse-invest.github.io/django-napse/#installation).
-### Run coverage tests
+### Documentation
-- Run tests \
-```test-napse```
-- Run tests with coverage \
-```coverage```
-- Run tests with coverage and open coverage report \
-```coverage-open```
+You can find the documentation [here](https://napse-invest.github.io/django-napse/).
-## Documentation
-[Docs](https://napse-invest.github.io/django-napse/)
+## How to contribute
-Run mkdocs server:
-```
-make mkdocs
-```
\ No newline at end of file
+If you want to contribute to the project, please read the [contributing guidelines](https://napse-invest.github.io/django-napse/contributing/) first. You will find the setup instructions and our standards.
\ No newline at end of file
diff --git a/django_napse/api/fleets/serializers/__init__.py b/django_napse/api/fleets/serializers/__init__.py
index b18113f7..deafb8a4 100644
--- a/django_napse/api/fleets/serializers/__init__.py
+++ b/django_napse/api/fleets/serializers/__init__.py
@@ -1 +1,2 @@
+from .cluster_serialisers import ClusterSerializer
from .fleet_serializers import FleetDetailSerializer, FleetSerializer
diff --git a/django_napse/api/fleets/serializers/cluster_serialisers.py b/django_napse/api/fleets/serializers/cluster_serialisers.py
new file mode 100644
index 00000000..2508c7c1
--- /dev/null
+++ b/django_napse/api/fleets/serializers/cluster_serialisers.py
@@ -0,0 +1,17 @@
+from rest_framework import serializers
+
+from django_napse.api.bots.serializers import BotSerializer
+from django_napse.core.models import Cluster
+
+
+class ClusterSerializer(serializers.ModelSerializer):
+ template_bot = BotSerializer()
+
+ class Meta:
+ model = Cluster
+ fields = [
+ "template_bot",
+ "share",
+ "breakpoint",
+ "autoscale",
+ ]
diff --git a/django_napse/api/fleets/serializers/fleet_serializers.py b/django_napse/api/fleets/serializers/fleet_serializers.py
index a2138c8d..d8fa5831 100644
--- a/django_napse/api/fleets/serializers/fleet_serializers.py
+++ b/django_napse/api/fleets/serializers/fleet_serializers.py
@@ -2,12 +2,18 @@
from rest_framework.fields import empty
from django_napse.api.bots.serializers import BotSerializer
+from django_napse.api.fleets.serializers.cluster_serialisers import ClusterSerializer
from django_napse.core.models import ConnectionWallet, Fleet
class FleetSerializer(serializers.ModelSerializer):
value = serializers.SerializerMethodField(read_only=True)
bot_count = serializers.SerializerMethodField(read_only=True)
+ clusters = ClusterSerializer(
+ write_only=True,
+ many=True,
+ required=True,
+ )
class Meta:
model = Fleet
@@ -20,8 +26,6 @@ class Meta:
]
read_only_fields = [
"uuid",
- "value",
- "bot_count",
]
def __init__(self, instance=None, data=empty, space=None, **kwargs):
diff --git a/django_napse/api/fleets/views/fleet_view.py b/django_napse/api/fleets/views/fleet_view.py
index 72831927..15afa269 100644
--- a/django_napse/api/fleets/views/fleet_view.py
+++ b/django_napse/api/fleets/views/fleet_view.py
@@ -5,7 +5,7 @@
from django_napse.api.custom_permissions import HasSpace
from django_napse.api.custom_viewset import CustomViewSet
from django_napse.api.fleets.serializers import FleetDetailSerializer, FleetSerializer
-from django_napse.core.models import NapseSpace
+from django_napse.core.models import Fleet, NapseSpace
class FleetView(CustomViewSet):
@@ -13,7 +13,10 @@ class FleetView(CustomViewSet):
serializer_class = FleetSerializer
def get_queryset(self):
- self.space = NapseSpace.objects.get(uuid=self.request.query_params["space"])
+ space_uuid = self.request.query_params.get("space", None)
+ if space_uuid is None:
+ return Fleet.objects.all()
+ self.space = NapseSpace.objects.get(uuid=space_uuid)
return self.space.fleets
def get_serialiser_class(self, *args, **kwargs):
@@ -25,6 +28,13 @@ def get_serialiser_class(self, *args, **kwargs):
result = actions.get(self.action)
return result if result else super().get_serializer_class()
+ def get_permissions(self):
+ match self.action:
+ case "list" | "create":
+ return [HasAPIKey()]
+ case _:
+ return super().get_permissions()
+
def list(self, request):
serializer = self.serializer_class(self.get_queryset(), many=True, space=self.space)
return Response(serializer.data, status=status.HTTP_200_OK)
@@ -34,6 +44,7 @@ def retrieve(self, request, pk=None):
def create(self, request, *args, **kwargs):
return Response(status=status.HTTP_501_NOT_IMPLEMENTED)
+ # serializer = self.serializer_class(data=request.data, space=self.space)
def delete(self):
return Response(status=status.HTTP_501_NOT_IMPLEMENTED)
diff --git a/django_napse/api/spaces/views/space_view.py b/django_napse/api/spaces/views/space_view.py
index 4a7da171..c34250cd 100644
--- a/django_napse/api/spaces/views/space_view.py
+++ b/django_napse/api/spaces/views/space_view.py
@@ -1,13 +1,11 @@
from rest_framework import status
-from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework_api_key.permissions import HasAPIKey
from django_napse.api.custom_permissions import HasFullAccessPermission, HasMasterKey, HasReadPermission
from django_napse.api.custom_viewset import CustomViewSet
-from django_napse.api.exchanges.serializers.exchange_account_serializer import ExchangeAccountSerializer
from django_napse.api.spaces.serializers import SpaceDetailSerializer, SpaceSerializer
-from django_napse.core.models import ExchangeAccount, NapseSpace
+from django_napse.core.models import NapseSpace
from django_napse.utils.errors import SpaceError
@@ -38,7 +36,7 @@ def get_permissions(self):
return [HasReadPermission()]
case "list":
return [HasAPIKey()]
- case "possible_exchange_accounts" | "create":
+ case "create":
return [HasMasterKey()]
case _:
@@ -82,11 +80,3 @@ def delete(self, request, *args, **kwargs):
except SpaceError.DeleteError:
return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)
return Response(status=status.HTTP_204_NO_CONTENT)
-
- @action(detail=False, methods=["GET"])
- def possible_exchange_accounts(self, request):
- serialized_exchange_account = ExchangeAccountSerializer(ExchangeAccount.objects.all(), many=True)
- return Response(
- serialized_exchange_account.data,
- status=status.HTTP_200_OK,
- )
diff --git a/django_napse/core/models/fleets/cluster.py b/django_napse/core/models/fleets/cluster.py
index fbfb026a..b75c17e4 100644
--- a/django_napse/core/models/fleets/cluster.py
+++ b/django_napse/core/models/fleets/cluster.py
@@ -8,8 +8,16 @@
class Cluster(models.Model):
- fleet = models.ForeignKey("Fleet", on_delete=models.CASCADE, related_name="clusters")
- template_bot = models.OneToOneField("Bot", on_delete=models.CASCADE, related_name="cluster")
+ fleet = models.ForeignKey(
+ "Fleet",
+ on_delete=models.CASCADE,
+ related_name="clusters",
+ )
+ template_bot = models.OneToOneField(
+ "Bot",
+ on_delete=models.CASCADE,
+ related_name="cluster",
+ )
share = models.FloatField()
breakpoint = models.FloatField()
autoscale = models.BooleanField()
diff --git a/django_napse/core/models/fleets/fleet.py b/django_napse/core/models/fleets/fleet.py
index 2def824e..c603847d 100644
--- a/django_napse/core/models/fleets/fleet.py
+++ b/django_napse/core/models/fleets/fleet.py
@@ -11,12 +11,25 @@
class Fleet(models.Model):
- uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
- name = models.CharField(max_length=100, default="Fleet")
- exchange_account = models.ForeignKey("ExchangeAccount", on_delete=models.CASCADE)
+ uuid = models.UUIDField(
+ default=uuid.uuid4,
+ editable=False,
+ unique=True,
+ )
+ name = models.CharField(
+ max_length=100,
+ default="Fleet",
+ )
+ exchange_account = models.ForeignKey(
+ "ExchangeAccount",
+ on_delete=models.CASCADE,
+ )
running = models.BooleanField(default=False)
setup_finished = models.BooleanField(default=False)
- created_at = models.DateTimeField(auto_now_add=True, blank=True)
+ created_at = models.DateTimeField(
+ auto_now_add=True,
+ blank=True,
+ )
objects = FleetManager()
diff --git a/django_napse/utils/api_test_case.py b/django_napse/utils/api_test_case.py
index ffd8db89..5db965b8 100644
--- a/django_napse/utils/api_test_case.py
+++ b/django_napse/utils/api_test_case.py
@@ -96,42 +96,42 @@ def run_tests(self, mode):
self.check_auth(name="No permissions", mode=mode, error_list=error_list, divider=100, expected=2)
else:
if HasSpace in permissions:
- self.check_auth(name="HasSpace \w no key", mode=mode, error_list=error_list, expected=400)
+ self.check_auth(name="HasSpace with no key", mode=mode, error_list=error_list, expected=400)
self.build_url(kwargs={"space": str("random uuid"), **self.kwargs})
- self.check_auth(name="HasSpace \w no key", mode=mode, error_list=error_list, expected=400)
+ self.check_auth(name="HasSpace with no key", mode=mode, error_list=error_list, expected=400)
self.build_url(kwargs={"space": str("7aafc68d-f619-4874-aaf5-c123a176e303"), **self.kwargs})
- self.check_auth(name="HasSpace \w no key", mode=mode, error_list=error_list, expected=400)
+ self.check_auth(name="HasSpace with no key", mode=mode, error_list=error_list, expected=400)
self.build_url(kwargs={"space": str(self.space.uuid), **self.kwargs})
if HasAPIKey in permissions:
- self.check_auth(name="HasAPIKey \w no key", mode=mode, error_list=error_list)
+ self.check_auth(name="HasAPIKey with no key", mode=mode, error_list=error_list)
if HasReadPermission in permissions:
- self.check_auth(name="HasReadPermission \w no key", mode=mode, error_list=error_list)
+ self.check_auth(name="HasReadPermission with no key", mode=mode, error_list=error_list)
self.build_key([])
self.authenticate()
- self.check_auth(name="HasReadPermission \w no permissions", mode=mode, error_list=error_list)
+ self.check_auth(name="HasReadPermission with no permissions", mode=mode, error_list=error_list)
if HasFullAccessPermission in permissions:
- self.check_auth(name="HasFullAccessPermission \w no key", mode=mode, error_list=error_list)
+ self.check_auth(name="HasFullAccessPermission with no key", mode=mode, error_list=error_list)
self.build_key([])
self.authenticate()
- self.check_auth(name="HasFullAccessPermission \w no permissions", mode=mode, error_list=error_list)
+ self.check_auth(name="HasFullAccessPermission with no permissions", mode=mode, error_list=error_list)
self.build_key([HasReadPermission])
self.authenticate()
- self.check_auth(name="HasFullAccessPermission \w read permissions", mode=mode, error_list=error_list)
+ self.check_auth(name="HasFullAccessPermission with read permissions", mode=mode, error_list=error_list)
if HasAdminPermission in permissions:
- self.check_auth(name="HasAdminPermission \w no key", mode=mode, error_list=error_list)
+ self.check_auth(name="HasAdminPermission with no key", mode=mode, error_list=error_list)
self.build_key([])
self.authenticate()
- self.check_auth(name="HasAdminPermission \w no permissions", mode=mode, error_list=error_list)
+ self.check_auth(name="HasAdminPermission with no permissions", mode=mode, error_list=error_list)
self.build_key([HasReadPermission])
self.authenticate()
- self.check_auth(name="HasAdminPermission \w read permissions", mode=mode, error_list=error_list)
+ self.check_auth(name="HasAdminPermission with read permissions", mode=mode, error_list=error_list)
self.build_key([HasReadPermission, HasFullAccessPermission])
self.authenticate()
- self.check_auth(name="HasAdminPermission \w read and full access permissions", mode=mode, error_list=error_list)
+ self.check_auth(name="HasAdminPermission with read and full access permissions", mode=mode, error_list=error_list)
self.build_key(permissions)
self.authenticate()
diff --git a/docs/contributing.md b/docs/contributing.md
index f270ac66..5c67726c 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -64,13 +64,19 @@ You can commit the code from your fork through a pull request on the official re
=== "Linux"
```bash
- source setup-unix.sh
+ source setup/setup-unix.sh
+ ```
+
+=== "MacOS"
+
+ ```bash
+ source setup/setup-osx.sh
```
=== "Windows"
```powershell
- .\setup-windows.ps1
+ .\setup\setup-windows.ps1
```
#### Setup initial exchange accounts
@@ -89,9 +95,11 @@ At `tests/test_app/`, build a `secret.json` file (or run the `./setup_secrets.sh
}
}
}
-
```
+!!! note
+ We **strongly recommend** to add the `secret.json` file to your `.gitignore` file to avoid sharing your API keys.
+
#### Run
```bash
diff --git a/docs/index.md b/docs/index.md
index 5de2077f..c3f8c100 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -22,13 +22,19 @@
# Welcome to django-napse's documentation!
+Django napse is a portfolio management module using trading bots.
+
## Installation
+---
+
+This project can be used as a django module, which you can install as follows:
-To install the latest version of django-napse:
```bash
pip install django-napse
```
+Or you can use it as a local backend for the [Napse desktop application](https://github.com/napse-invest/Napse), by cloning the repo (possibly after forking it).
+
### Setup initial exchange accounts
To make full use of the project, we recommend that you fill in the API keys of at least one exchange (among the django-napse [compabile exchanges](#compatible-exchanges)).
@@ -38,7 +44,7 @@ At the root of your project, build a `secret.json` file. Here is an exemple with
{
"Exchange Accounts": {
"Binance EA_NAME": {
- "exchange": "BINANCE", # Name of your exchange (BINANCE, DYDX, ...)
+ "exchange": "BINANCE", # Name of your exchange
"testing": true,
"public_key": "YOUR_PUBLIC_KEY",
"private_key": "YOUR_PRIVATE_KEY"
@@ -46,10 +52,27 @@ At the root of your project, build a `secret.json` file. Here is an exemple with
}
}
```
-We **strongly** recommand you to add the `secret.json` file to your `.gitignore`.
-
+??? note "Note for developers"
+ We **strongly recommend** to add the `secret.json` file to your `.gitignore` file to avoid sharing your API keys.
## Use django-napse
+---
+
+### Local backend
+
+If you want to use django-napse as a local backend for the Napse desktop application, clone the repository and setup the project:
+```bash
+make setup
+```
+
+Then, you can run the server:
+```bash
+make up
+```
+
+Please check the documentation for more information about [endpoints](https://napse-invest.github.io/django-napse/api/).
+
+### Django module
After the installation step, in a `.py` file you can use django-napse after importing it:
```python
@@ -59,6 +82,7 @@ exchange_account_query = ExchangeAccount.objects.all()
```
## Miscellaneous
+---
### Compatible exchanges
diff --git a/docs/api.md b/docs/sources/api.md
similarity index 100%
rename from docs/api.md
rename to docs/sources/api.md
diff --git a/docs/sources/reference/spaces.md b/docs/sources/reference/spaces.md
index 725e8635..97e99d40 100644
--- a/docs/sources/reference/spaces.md
+++ b/docs/sources/reference/spaces.md
@@ -1,8 +1,6 @@
# Space
## Space's Model
::: django_napse.core.models.NapseSpace
- options:
-
---
## Space's manager
diff --git a/docs/theme/assets/stylesheets/docstring.css b/docs/theme/assets/stylesheets/docstring.css
deleted file mode 100644
index eb51d691..00000000
--- a/docs/theme/assets/stylesheets/docstring.css
+++ /dev/null
@@ -1,83 +0,0 @@
-.tile {
-display: flex;
-text-align: center;
-width: 120px;
-height: 120px;
-display: inline-block;
-margin: 10px;
-padding: 5px;
-border-radius: .5rem;
-}
-
-.tile img {
-width: 100px;
-}
-
-.md-typeset__table > table {
-max-height: 60vh;
-}
-
-.md-typeset__table > table thead {
-position: sticky;
-top: 0;
-background-color: var(--md-default-bg-color);
-}
-
-.md-typeset__table > table th {
-border-bottom: .05rem solid var(--md-typeset-table-color);
-}
-
-.md-typeset__table > table tr:first-child td {
-border-top: none;
-}
-
-/* API documentation link admonition */
-:root {
---md-admonition-icon--api: url('data:image/svg+xml;charset=utf-8,')
-}
-.md-typeset .admonition.api, .md-typeset details.api {
-border-color: #448aff;
-}
-.md-typeset .api > .admonition-title, .md-typeset .api > summary {
-background-color: #448aff1a;
-}
-.md-typeset .api > .admonition-title::before, .md-typeset .api > summary::before {
-background-color: #448aff;
--webkit-mask-image: var(--md-admonition-icon--api);
- mask-image: var(--md-admonition-icon--api);
-}
-
-/* Revert hue value to that of pre mkdocs-material v9.4.0 */
-[data-md-color-scheme="slate"] {
---md-hue: 230;
---md-default-bg-color: hsla(230, 15%, 21%, 1);
-}
-
-
- /* Indentation. */
-div.doc-contents:not(.first) {
- padding-left: 25px;
- border-left: .05rem solid var(--md-typeset-table-color);
-}
-
-/* Mark external links as such. */
-a.external::after,
-a.autorefs-external::after {
- /* https://primer.style/octicons/arrow-up-right-24 */
- mask-image: url('data:image/svg+xml,');
- -webkit-mask-image: url('data:image/svg+xml,');
- content: ' ';
-
- display: inline-block;
- vertical-align: middle;
- position: relative;
-
- height: 1em;
- width: 1em;
- background-color: var(--md-typeset-a-color);
-}
-
-a.external:hover::after,
-a.autorefs-external:hover::after {
- background-color: var(--md-accent-fg-color);
-}
diff --git a/docs/theme/partials/header.html b/docs/theme/partials/header.html
deleted file mode 100644
index d301d733..00000000
--- a/docs/theme/partials/header.html
+++ /dev/null
@@ -1,63 +0,0 @@
-{#-
- This file was automatically generated - do not edit
- -#}
- {% set class = "md-header" %}
- {% if "navigation.tabs.sticky" in features %}
- {% set class = class ~ " md-header--shadow md-header--lifted" %}
- {% elif "navigation.tabs" not in features %}
- {% set class = class ~ " md-header--shadow" %}
- {% endif %}
-
-
- {% if "navigation.tabs.sticky" in features %}
- {% if "navigation.tabs" in features %}
- {% include "partials/tabs.html" %}
- {% endif %}
- {% endif %}
-
\ No newline at end of file
diff --git a/mkdocs.yml b/mkdocs.yml
index c719f9cb..b5fc1435 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -167,8 +167,8 @@ nav:
- "sources/reference/spaces.md"
- "sources/reference/transactions.md"
- "sources/reference/wallets.md"
- - API: "api.md"
+ - API: "sources/api.md"
- Development:
- Contributing: "contributing.md"
- Security: "SECURITY.md"
- - Coverage: coverage.md
\ No newline at end of file
+ - Coverage: coverage.md
diff --git a/setup-osx.sh b/setup/setup-osx.sh
similarity index 100%
rename from setup-osx.sh
rename to setup/setup-osx.sh
diff --git a/setup-unix.sh b/setup/setup-unix.sh
similarity index 100%
rename from setup-unix.sh
rename to setup/setup-unix.sh
diff --git a/setup-windows.ps1 b/setup/setup-windows.ps1
similarity index 100%
rename from setup-windows.ps1
rename to setup/setup-windows.ps1