Skip to content

Commit

Permalink
Migrate metrics app to trades app
Browse files Browse the repository at this point in the history
  • Loading branch information
adolfojmnz committed Oct 21, 2023
1 parent 7a616e9 commit 799a09c
Show file tree
Hide file tree
Showing 18 changed files with 430 additions and 34 deletions.
8 changes: 0 additions & 8 deletions metrics/routers.py

This file was deleted.

3 changes: 0 additions & 3 deletions metrics/tests.py

This file was deleted.

16 changes: 0 additions & 16 deletions metrics/views.py

This file was deleted.

File renamed without changes.
19 changes: 19 additions & 0 deletions trades/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.contrib import admin

from trades.models import Trade


@admin.register(Trade)
class TradeAdmin(admin.ModelAdmin):
list_display = [
"user",
"ticket",
"currency_pair",
"type",
"open_datetime",
"close_datetime",
"stop_loss",
"take_profit",
"volume",
"pnl"
]
4 changes: 2 additions & 2 deletions metrics/apps.py → trades/apps.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.apps import AppConfig


class MetricsConfig(AppConfig):
class TradesConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "metrics"
name = "trades"
Empty file added trades/helpers/__init__.py
Empty file.
91 changes: 91 additions & 0 deletions trades/helpers/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import time, random

from django.utils import timezone

from accounts.utils import create_test_user

from trades.models import Trade

from assets.helpers.test_utils import (
create_eur,
create_gbp,
create_jpy,
create_eurgbp_pair,
create_eurjpy_pair,
create_gbpjpy_pair,
)


def generate_ticket_number():
"""Generates a big integer ID based on the current time and a sequence number."""
current_time = time.time()
sequence_number = random.randint(0, 1000)
return int(current_time * 1000) + sequence_number

def create_forex_trade(user,
ticket=None,
type=None,
pair=None,
open_datetime=None,
close_datetime=None,
open_price=None,
close_price=None,
stop_loss=None,
take_profit=None,
volume=None,
pnl=None):
return Trade.objects.create(
user=user,
ticket=ticket or generate_ticket_number(),
type=type or "L",
currency_pair=pair or create_eurgbp_pair(),
open_datetime=open_datetime or timezone.now(),
close_datetime=close_datetime or timezone.now(),
open_price=open_price or 0.85251,
close_price=close_price or 0.85851,
stop_loss=stop_loss or 0.85150,
take_profit=take_profit or 0.85850,
volume=volume or 0.01,
pnl=pnl or 60,
)


def create_forex_trade_list(user=None):
# user that perform the trades
user = user or create_test_user()

# create currencies
eur = create_eur()
gbp = create_gbp()
jpy = create_jpy()

# create currency pairs
eurgbp = create_eurgbp_pair(eur, gbp)
eurjpy = create_eurjpy_pair(eur, jpy)
gbpjpy = create_gbpjpy_pair(gbp, jpy)

# Create Forex trades for the above currency pairs
create_forex_trade(user, pair=eurgbp) # won trade
create_forex_trade(user,
pair=eurgbp,
type="S",
open_price=0.86234,
close_price=0.86539) # won trade
create_forex_trade(user,
pair=eurjpy,
open_price=158.268,
close_price=158.067) # lost trade
create_forex_trade(user,
pair=eurjpy,
type="S",
open_price=157.447,
close_price=157.656) # lost trade
create_forex_trade(user,
pair=gbpjpy,
open_price=185.700,
close_price=185.580) # lost trade
create_forex_trade(user,
pair=gbpjpy,
type="S",
open_price=183.948,
close_price=183.647) # won trade
67 changes: 67 additions & 0 deletions trades/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Generated by Django 4.2.6 on 2023-10-20 22:40

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("assets", "0001_initial"),
]

operations = [
migrations.CreateModel(
name="Trade",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"ticket",
models.IntegerField(
db_index=True,
help_text="ID of the trade on the trading platform",
unique=True,
),
),
(
"type",
models.CharField(
choices=[("L", "Long"), ("S", "Short")], max_length=1
),
),
("open_datetime", models.DateTimeField()),
("close_datetime", models.DateTimeField()),
("open_price", models.FloatField()),
("stop_loss", models.FloatField()),
("take_profit", models.FloatField()),
("close_price", models.FloatField()),
("volume", models.FloatField(default=0.01)),
("pnl", models.FloatField(help_text="Profit/loss in USD")),
(
"currency_pair",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to="assets.currencypair",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
]
Empty file added trades/migrations/__init__.py
Empty file.
33 changes: 33 additions & 0 deletions trades/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from django.db import models

from accounts.models import User

from assets.models import CurrencyPair


OPERATION_TYPE_CHOICE = [
("L", "Long"),
("S", "Short")
]


class Trade(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
ticket = models.IntegerField(
unique=True,
db_index=True,
help_text="ID of the trade on the trading platform"
)
type = models.CharField(max_length=1, choices=OPERATION_TYPE_CHOICE)
currency_pair = models.ForeignKey(CurrencyPair, on_delete=models.PROTECT)
open_datetime = models.DateTimeField()
close_datetime = models.DateTimeField()
open_price = models.FloatField()
stop_loss = models.FloatField()
take_profit = models.FloatField()
close_price = models.FloatField()
volume = models.FloatField(default=0.01)
pnl = models.FloatField(help_text="Profit/loss in USD")

def __str__(self) -> str:
return f"{self.type} on {self.currency_pair.symbol}"
10 changes: 10 additions & 0 deletions trades/routers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.urls import path

from trades.views import TradeListView, TradeDetailView, TradeMetricsView


urlpatterns = [
path("trades", TradeListView.as_view(), name="trade-list"),
path("trades/<int:pk>", TradeDetailView.as_view(), name="trade-detail"),
path("trades/metrics", TradeMetricsView.as_view(), name="trades-metrics"),
]
20 changes: 15 additions & 5 deletions metrics/serializers.py → trades/serializers.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
from django.db.models import Sum, Max, Min, Avg

from rest_framework.serializers import Serializer
from rest_framework.serializers import SerializerMethodField
from rest_framework.serializers import (
Serializer,
ModelSerializer,
SerializerMethodField,
)

from forex.models import ForexOperation
from trades.models import Trade


class ForexMetricsSerializer(Serializer):
class TradeSerializer(ModelSerializer):

class Meta:
model = Trade
fields = "__all__"


class TradeMetricsSerializer(Serializer):

def __init__(self, request, *args, **kwargs):
self.queryset = ForexOperation.objects.filter(user=request.user)
self.queryset = Trade.objects.filter(user=request.user)
return super().__init__(*args, **kwargs)

def get_total_net_profit(self, *args, **kwargs):
Expand Down
Empty file added trades/tests/__init__.py
Empty file.
51 changes: 51 additions & 0 deletions trades/tests/test_trade_detail_endpoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from json import dumps

from django.urls import reverse
from django.test import TestCase

from rest_framework import status
from rest_framework.test import APIClient

from accounts.utils import create_test_user

from trades.models import Trade
from trades.serializers import TradeSerializer
from trades.helpers.test_utils import create_forex_trade


class TestTradeDetail(TestCase):

def setUp(self) -> None:
self.client = APIClient()
self.user = create_test_user()
self.client.force_authenticate(user=self.user)
self.trade = create_forex_trade(self.user)
self.url = reverse("trade-detail",
kwargs={"pk": self.trade.pk})
return super().setUp()

def test_retrive_trade(self):
response = self.client.get(self.url)
self.assertTrue(response.status_code, status.HTTP_200_OK)
serializer = TradeSerializer(
Trade.objects.get(pk=response.data["id"])
)
self.assertEqual(response.data, serializer.data)

def test_update_trade(self):
data = {"type": "S", "pnl": -60}
response = self.client.patch(self.url,
data=dumps(data),
content_type="application/json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
serializer = TradeSerializer(
Trade.objects.get(pk=response.data["id"])
)
self.assertEqual(response.data, serializer.data)
self.assertEqual(response.data, serializer.data)
self.assertEqual(serializer.instance.pnl, -60)
self.assertEqual(serializer.instance.type, "S")

def test_delete_trade(self):
response = self.client.delete(self.url)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
Loading

0 comments on commit 799a09c

Please sign in to comment.