First Trading with SaaS 2025-12-28
This commit is contained in:
parent
734c24304e
commit
2d3a86ad45
Binary file not shown.
Binary file not shown.
@ -56,6 +56,7 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'core',
|
'core',
|
||||||
|
'signals',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|||||||
@ -22,6 +22,7 @@ from django.conf.urls.static import static
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
path("", include("core.urls")),
|
path("", include("core.urls")),
|
||||||
|
path("api/", include("signals.urls")),
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
Django==5.2.7
|
Django==5.2.7
|
||||||
mysqlclient==2.2.7
|
mysqlclient==2.2.7
|
||||||
python-dotenv==1.1.1
|
python-dotenv==1.1.1
|
||||||
|
ccxt
|
||||||
|
|||||||
0
signals/__init__.py
Normal file
0
signals/__init__.py
Normal file
BIN
signals/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
signals/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
signals/__pycache__/admin.cpython-311.pyc
Normal file
BIN
signals/__pycache__/admin.cpython-311.pyc
Normal file
Binary file not shown.
BIN
signals/__pycache__/apps.cpython-311.pyc
Normal file
BIN
signals/__pycache__/apps.cpython-311.pyc
Normal file
Binary file not shown.
BIN
signals/__pycache__/engine.cpython-311.pyc
Normal file
BIN
signals/__pycache__/engine.cpython-311.pyc
Normal file
Binary file not shown.
BIN
signals/__pycache__/models.cpython-311.pyc
Normal file
BIN
signals/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
BIN
signals/__pycache__/urls.cpython-311.pyc
Normal file
BIN
signals/__pycache__/urls.cpython-311.pyc
Normal file
Binary file not shown.
BIN
signals/__pycache__/views.cpython-311.pyc
Normal file
BIN
signals/__pycache__/views.cpython-311.pyc
Normal file
Binary file not shown.
3
signals/admin.py
Normal file
3
signals/admin.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
6
signals/apps.py
Normal file
6
signals/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class SignalsConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'signals'
|
||||||
67
signals/engine.py
Normal file
67
signals/engine.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
|
||||||
|
import ccxt
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def get_ohlcv(symbol, timeframe):
|
||||||
|
exchange = ccxt.binance()
|
||||||
|
ohlcv = exchange.fetch_ohlcv(symbol, timeframe)
|
||||||
|
df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
||||||
|
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
|
||||||
|
return df
|
||||||
|
|
||||||
|
def get_trend(df):
|
||||||
|
df['ema20'] = df['close'].ewm(span=20, adjust=False).mean()
|
||||||
|
df['ema50'] = df['close'].ewm(span=50, adjust=False).mean()
|
||||||
|
df['rsi'] = 100 - (100 / (1 + df['close'].diff().apply(lambda x: x if x > 0 else 0).ewm(alpha=1/14, adjust=False).mean() / df['close'].diff().apply(lambda x: abs(x) if x < 0 else 0).ewm(alpha=1/14, adjust=False).mean()))
|
||||||
|
|
||||||
|
last_row = df.iloc[-1]
|
||||||
|
if last_row['ema20'] > last_row['ema50'] and last_row['close'] > last_row['ema20'] and last_row['rsi'] > 50:
|
||||||
|
return 'UP'
|
||||||
|
elif last_row['ema20'] < last_row['ema50'] and last_row['close'] < last_row['ema20'] and last_row['rsi'] < 50:
|
||||||
|
return 'DOWN'
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_setup(df):
|
||||||
|
df['rsi'] = 100 - (100 / (1 + df['close'].diff().apply(lambda x: x if x > 0 else 0).ewm(alpha=1/14, adjust=False).mean() / df['close'].diff().apply(lambda x: abs(x) if x < 0 else 0).ewm(alpha=1/14, adjust=False).mean()))
|
||||||
|
|
||||||
|
last_row = df.iloc[-1]
|
||||||
|
if last_row['rsi'] > 40 and last_row['rsi'] < 60:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_entry(df):
|
||||||
|
df['ema20'] = df['close'].ewm(span=20, adjust=False).mean()
|
||||||
|
df['ema50'] = df['close'].ewm(span=50, adjust=False).mean()
|
||||||
|
df['rsi'] = 100 - (100 / (1 + df['close'].diff().apply(lambda x: x if x > 0 else 0).ewm(alpha=1/14, adjust=False).mean() / df['close'].diff().apply(lambda x: abs(x) if x < 0 else 0).ewm(alpha=1/14, adjust=False).mean()))
|
||||||
|
df['volume_sma20'] = df['volume'].rolling(window=20).mean()
|
||||||
|
|
||||||
|
last_row = df.iloc[-1]
|
||||||
|
if last_row['ema20'] > last_row['ema50'] and last_row['rsi'] > 50 and last_row['close'] > last_row['ema20'] and last_row['volume'] > last_row['volume_sma20']:
|
||||||
|
return 'BUY'
|
||||||
|
elif last_row['ema20'] < last_row['ema50'] and last_row['rsi'] < 50 and last_row['close'] < last_row['ema20'] and last_row['volume'] > last_row['volume_sma20']:
|
||||||
|
return 'SELL'
|
||||||
|
else:
|
||||||
|
return 'WAIT'
|
||||||
|
|
||||||
|
def generate_signal(symbol):
|
||||||
|
# 1h trend
|
||||||
|
df_1h = get_ohlcv(symbol, '1h')
|
||||||
|
trend = get_trend(df_1h)
|
||||||
|
|
||||||
|
if trend:
|
||||||
|
# 15m setup
|
||||||
|
df_15m = get_ohlcv(symbol, '15m')
|
||||||
|
setup = get_setup(df_15m)
|
||||||
|
|
||||||
|
if setup:
|
||||||
|
# 5m entry
|
||||||
|
df_5m = get_ohlcv(symbol, '5m')
|
||||||
|
entry = get_entry(df_5m)
|
||||||
|
|
||||||
|
if (trend == 'UP' and entry == 'BUY') or (trend == 'DOWN' and entry == 'SELL'):
|
||||||
|
return entry
|
||||||
|
|
||||||
|
return 'WAIT'
|
||||||
|
|
||||||
47
signals/migrations/0001_initial.py
Normal file
47
signals/migrations/0001_initial.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-12-28 14:00
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Backtest',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('symbol', models.CharField(max_length=20)),
|
||||||
|
('start_date', models.DateTimeField()),
|
||||||
|
('end_date', models.DateTimeField()),
|
||||||
|
('profit_loss', models.FloatField()),
|
||||||
|
('win_rate', models.FloatField()),
|
||||||
|
('max_drawdown', models.FloatField()),
|
||||||
|
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Signal',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('symbol', models.CharField(max_length=20)),
|
||||||
|
('signal', models.CharField(choices=[('BUY', 'Buy'), ('SELL', 'Sell'), ('WAIT', 'Wait')], max_length=4)),
|
||||||
|
('timeframe', models.CharField(max_length=10)),
|
||||||
|
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ConfidenceScore',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('score', models.FloatField()),
|
||||||
|
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('signal', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='confidence_score', to='signals.signal')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
0
signals/migrations/__init__.py
Normal file
0
signals/migrations/__init__.py
Normal file
BIN
signals/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
BIN
signals/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
Binary file not shown.
BIN
signals/migrations/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
signals/migrations/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
36
signals/models.py
Normal file
36
signals/models.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Signal(models.Model):
|
||||||
|
SIGNAL_CHOICES = [
|
||||||
|
('BUY', 'Buy'),
|
||||||
|
('SELL', 'Sell'),
|
||||||
|
('WAIT', 'Wait'),
|
||||||
|
]
|
||||||
|
|
||||||
|
symbol = models.CharField(max_length=20)
|
||||||
|
signal = models.CharField(max_length=4, choices=SIGNAL_CHOICES)
|
||||||
|
timeframe = models.CharField(max_length=10)
|
||||||
|
timestamp = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.symbol} - {self.signal} ({self.timeframe})"
|
||||||
|
|
||||||
|
class ConfidenceScore(models.Model):
|
||||||
|
signal = models.OneToOneField(Signal, on_delete=models.CASCADE, related_name='confidence_score')
|
||||||
|
score = models.FloatField()
|
||||||
|
timestamp = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.signal} - {self.score}"
|
||||||
|
|
||||||
|
class Backtest(models.Model):
|
||||||
|
symbol = models.CharField(max_length=20)
|
||||||
|
start_date = models.DateTimeField()
|
||||||
|
end_date = models.DateTimeField()
|
||||||
|
profit_loss = models.FloatField()
|
||||||
|
win_rate = models.FloatField()
|
||||||
|
max_drawdown = models.FloatField()
|
||||||
|
timestamp = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Backtest for {self.symbol} from {self.start_date} to {self.end_date}"
|
||||||
3
signals/tests.py
Normal file
3
signals/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
9
signals/urls.py
Normal file
9
signals/urls.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
app_name = 'signals'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('signal/<str:symbol>/', views.get_signal, name='get_signal'),
|
||||||
|
]
|
||||||
6
signals/views.py
Normal file
6
signals/views.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.http import JsonResponse
|
||||||
|
from .engine import generate_signal
|
||||||
|
|
||||||
|
def get_signal(request, symbol):
|
||||||
|
signal = generate_signal(symbol)
|
||||||
|
return JsonResponse({'symbol': symbol, 'signal': signal})
|
||||||
0
trading/__init__.py
Normal file
0
trading/__init__.py
Normal file
BIN
trading/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
trading/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
trading/__pycache__/admin.cpython-311.pyc
Normal file
BIN
trading/__pycache__/admin.cpython-311.pyc
Normal file
Binary file not shown.
BIN
trading/__pycache__/apps.cpython-311.pyc
Normal file
BIN
trading/__pycache__/apps.cpython-311.pyc
Normal file
Binary file not shown.
BIN
trading/__pycache__/models.cpython-311.pyc
Normal file
BIN
trading/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
BIN
trading/__pycache__/signals.cpython-311.pyc
Normal file
BIN
trading/__pycache__/signals.cpython-311.pyc
Normal file
Binary file not shown.
26
trading/admin.py
Normal file
26
trading/admin.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.admin import UserAdmin
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from .models import UserProfile, Signal, Trade, Backtest
|
||||||
|
|
||||||
|
class UserProfileInline(admin.StackedInline):
|
||||||
|
model = UserProfile
|
||||||
|
can_delete = False
|
||||||
|
verbose_name_plural = 'Profile'
|
||||||
|
fk_name = 'user'
|
||||||
|
|
||||||
|
class CustomUserAdmin(UserAdmin):
|
||||||
|
inlines = (UserProfileInline, )
|
||||||
|
|
||||||
|
def get_inline_instances(self, request, obj=None):
|
||||||
|
if not obj:
|
||||||
|
return list()
|
||||||
|
return super(CustomUserAdmin, self).get_inline_instances(request, obj)
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.unregister(User)
|
||||||
|
admin.site.register(User, CustomUserAdmin)
|
||||||
|
|
||||||
|
admin.site.register(Signal)
|
||||||
|
admin.site.register(Trade)
|
||||||
|
admin.site.register(Backtest)
|
||||||
9
trading/apps.py
Normal file
9
trading/apps.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class TradingConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'trading'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
import trading.signals
|
||||||
59
trading/migrations/0001_initial.py
Normal file
59
trading/migrations/0001_initial.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-12-28 13:55
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Signal',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('symbol', models.CharField(max_length=20)),
|
||||||
|
('signal', models.CharField(choices=[('BUY', 'Buy'), ('SELL', 'Sell'), ('WAIT', 'Wait')], max_length=4)),
|
||||||
|
('confidence', models.FloatField()),
|
||||||
|
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Backtest',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('start_date', models.DateTimeField()),
|
||||||
|
('end_date', models.DateTimeField()),
|
||||||
|
('results', models.JSONField()),
|
||||||
|
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Trade',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('symbol', models.CharField(max_length=20)),
|
||||||
|
('entry_price', models.FloatField()),
|
||||||
|
('exit_price', models.FloatField(blank=True, null=True)),
|
||||||
|
('entry_timestamp', models.DateTimeField()),
|
||||||
|
('exit_timestamp', models.DateTimeField(blank=True, null=True)),
|
||||||
|
('signal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='trading.signal')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserProfile',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('role', models.CharField(choices=[('Free', 'Free'), ('Pro', 'Pro'), ('Admin', 'Admin')], default='Free', max_length=10)),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
0
trading/migrations/__init__.py
Normal file
0
trading/migrations/__init__.py
Normal file
BIN
trading/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
BIN
trading/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
Binary file not shown.
BIN
trading/migrations/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
trading/migrations/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
50
trading/models.py
Normal file
50
trading/models.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
class UserProfile(models.Model):
|
||||||
|
USER_ROLE_CHOICES = (
|
||||||
|
('Free', 'Free'),
|
||||||
|
('Pro', 'Pro'),
|
||||||
|
('Admin', 'Admin'),
|
||||||
|
)
|
||||||
|
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
|
||||||
|
role = models.CharField(max_length=10, choices=USER_ROLE_CHOICES, default='Free')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.user.username} - {self.role}'
|
||||||
|
|
||||||
|
class Signal(models.Model):
|
||||||
|
SIGNAL_CHOICES = (
|
||||||
|
('BUY', 'Buy'),
|
||||||
|
('SELL', 'Sell'),
|
||||||
|
('WAIT', 'Wait'),
|
||||||
|
)
|
||||||
|
symbol = models.CharField(max_length=20)
|
||||||
|
signal = models.CharField(max_length=4, choices=SIGNAL_CHOICES)
|
||||||
|
confidence = models.FloatField()
|
||||||
|
timestamp = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.timestamp} - {self.symbol} - {self.signal}'
|
||||||
|
|
||||||
|
class Trade(models.Model):
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
symbol = models.CharField(max_length=20)
|
||||||
|
entry_price = models.FloatField()
|
||||||
|
exit_price = models.FloatField(null=True, blank=True)
|
||||||
|
entry_timestamp = models.DateTimeField()
|
||||||
|
exit_timestamp = models.DateTimeField(null=True, blank=True)
|
||||||
|
signal = models.ForeignKey(Signal, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.user.username} - {self.symbol}'
|
||||||
|
|
||||||
|
class Backtest(models.Model):
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
start_date = models.DateTimeField()
|
||||||
|
end_date = models.DateTimeField()
|
||||||
|
results = models.JSONField()
|
||||||
|
timestamp = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.user.username} - {self.timestamp}'
|
||||||
16
trading/signals.py
Normal file
16
trading/signals.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
from django.db.models.signals import post_save
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from .models import UserProfile
|
||||||
|
|
||||||
|
@receiver(post_save, sender=User)
|
||||||
|
def create_user_profile(sender, instance, created, **kwargs):
|
||||||
|
if created:
|
||||||
|
UserProfile.objects.create(user=instance)
|
||||||
|
|
||||||
|
@receiver(post_save, sender=User)
|
||||||
|
def save_user_profile(sender, instance, **kwargs):
|
||||||
|
try:
|
||||||
|
instance.profile.save()
|
||||||
|
except UserProfile.DoesNotExist:
|
||||||
|
UserProfile.objects.create(user=instance)
|
||||||
3
trading/tests.py
Normal file
3
trading/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
3
trading/views.py
Normal file
3
trading/views.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
Loading…
x
Reference in New Issue
Block a user