-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7a616e9
commit 799a09c
Showing
18 changed files
with
430 additions
and
34 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Oops, something went wrong.