First Trading with SaaS 2025-12-28

This commit is contained in:
Flatlogic Bot 2025-12-28 21:36:27 +00:00
parent 734c24304e
commit 2d3a86ad45
40 changed files with 346 additions and 0 deletions

View File

@ -56,6 +56,7 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'core',
'signals',
]
MIDDLEWARE = [

View File

@ -22,6 +22,7 @@ from django.conf.urls.static import static
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("core.urls")),
path("api/", include("signals.urls")),
]
if settings.DEBUG:

View File

@ -1,3 +1,4 @@
Django==5.2.7
mysqlclient==2.2.7
python-dotenv==1.1.1
ccxt

0
signals/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
signals/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
signals/apps.py Normal file
View 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
View 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'

View 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')),
],
),
]

View File

36
signals/models.py Normal file
View 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
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

9
signals/urls.py Normal file
View 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
View 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
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

26
trading/admin.py Normal file
View 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
View 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

View 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)),
],
),
]

View File

50
trading/models.py Normal file
View 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
View 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
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
trading/views.py Normal file
View File

@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.