From 617b485a0147266d93ec7db22afd2cd9055b0c09 Mon Sep 17 00:00:00 2001 From: lxf Date: Mon, 3 Jan 2022 17:02:50 -0500 Subject: trad: new options tracker that works better for WON system --- app/trading/admin.py | 25 ++++++- app/trading/forms.py | 25 +++++++ .../0017_luxoptioncontact_luxoptionpurchase.py | 34 ++++++++++ .../migrations/0018_luxoptionpurchase_pl.py | 18 +++++ app/trading/migrations/0019_auto_20220103_1458.py | 32 +++++++++ app/trading/migrations/0020_auto_20220103_1517.py | 23 +++++++ app/trading/models.py | 73 +++++++++++++++++++- .../templates/trading/create_luxoptions_form.html | 68 +++++++++++++++++++ app/trading/templates/trading/list.html | 77 +++++++++++++++++++++- app/trading/urls.py | 7 +- app/trading/views.py | 39 ++++++++++- 11 files changed, 414 insertions(+), 7 deletions(-) create mode 100644 app/trading/forms.py create mode 100644 app/trading/migrations/0017_luxoptioncontact_luxoptionpurchase.py create mode 100644 app/trading/migrations/0018_luxoptionpurchase_pl.py create mode 100644 app/trading/migrations/0019_auto_20220103_1458.py create mode 100644 app/trading/migrations/0020_auto_20220103_1517.py create mode 100644 app/trading/templates/trading/create_luxoptions_form.html (limited to 'app') diff --git a/app/trading/admin.py b/app/trading/admin.py index 2e4e05c..338c924 100644 --- a/app/trading/admin.py +++ b/app/trading/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin from django.db import models -from .models import TradeJrnl, LuxOptionsTrade, LuxTrade +from .models import TradeJrnl, LuxOptionsTrade, LuxTrade, LuxOptionPurchase, LuxOptionContact from utils.widgets import AdminImageWidget, LGEntryForm @@ -34,3 +34,26 @@ class LuxTradeAdmin(admin.ModelAdmin): "all": ("my_styles.css",) } + +class LuxOptionContactInline(admin.StackedInline): + model = LuxOptionContact + extra = 0 + fieldsets = ( + (None, { + 'fields': ( + 'contract_open_price', + 'contract_close_price' + ) + }), + ) + + +@admin.register(LuxOptionPurchase) +class LuxOptionPurchaseAdmin(admin.ModelAdmin): + list_display = ('symbol',) + inlines = [LuxOptionContactInline,] + class Media: + js = ('image-loader.js', 'next-prev-links.js') + css = { + "all": ("my_styles.css",) + } diff --git a/app/trading/forms.py b/app/trading/forms.py new file mode 100644 index 0000000..60c6de6 --- /dev/null +++ b/app/trading/forms.py @@ -0,0 +1,25 @@ +from django import forms +from django.forms.utils import ValidationError + +from .models import LuxOptionContact + +class LuxOptionsForm(forms.ModelForm): + contracts = forms.IntegerField() + + class Meta: + model = LuxOptionContact + fields = [ + 'symbol', + 'strike_price', + 'expiration_date', + 'contract_open_price', + 'call_put', + 'contracts' + ] + + def save(self, commit=True): + i = 0 + while i < int(self.cleaned_data['contracts']): + print(i, "contracts") + i = i+1 + return super(LuxOptionsForm, self).save(commit=commit) diff --git a/app/trading/migrations/0017_luxoptioncontact_luxoptionpurchase.py b/app/trading/migrations/0017_luxoptioncontact_luxoptionpurchase.py new file mode 100644 index 0000000..2258466 --- /dev/null +++ b/app/trading/migrations/0017_luxoptioncontact_luxoptionpurchase.py @@ -0,0 +1,34 @@ +# Generated by Django 3.2.7 on 2022-01-03 13:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0016_alter_luxoptionstrade_pl'), + ] + + operations = [ + migrations.CreateModel( + name='LuxOptionContact', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('symbol', models.CharField(max_length=256)), + ('strike_price', models.FloatField()), + ('expiration_date', models.DateField()), + ('contract_open_price', models.FloatField()), + ('contract_close_price', models.FloatField(blank=True, null=True)), + ('call_put', models.IntegerField(choices=[(0, 'Call'), (1, 'Put')], default=0)), + ], + ), + migrations.CreateModel( + name='LuxOptionPurchase', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('symbol', models.CharField(max_length=256)), + ('open_date', models.DateTimeField(auto_now_add=True)), + ('contracts', models.ManyToManyField(to='trading.LuxOptionContact')), + ], + ), + ] diff --git a/app/trading/migrations/0018_luxoptionpurchase_pl.py b/app/trading/migrations/0018_luxoptionpurchase_pl.py new file mode 100644 index 0000000..46d3b66 --- /dev/null +++ b/app/trading/migrations/0018_luxoptionpurchase_pl.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.7 on 2022-01-03 14:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0017_luxoptioncontact_luxoptionpurchase'), + ] + + operations = [ + migrations.AddField( + model_name='luxoptionpurchase', + name='pl', + field=models.FloatField(blank=True, null=True), + ), + ] diff --git a/app/trading/migrations/0019_auto_20220103_1458.py b/app/trading/migrations/0019_auto_20220103_1458.py new file mode 100644 index 0000000..d0d6529 --- /dev/null +++ b/app/trading/migrations/0019_auto_20220103_1458.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2.7 on 2022-01-03 14:58 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0018_luxoptionpurchase_pl'), + ] + + operations = [ + migrations.AlterModelOptions( + name='luxoptionpurchase', + options={'get_latest_by': 'open_date', 'ordering': ('-open_date',)}, + ), + migrations.RemoveField( + model_name='luxoptionpurchase', + name='contracts', + ), + migrations.AddField( + model_name='luxoptioncontact', + name='options_purchase', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='trading.luxoptionpurchase'), + ), + migrations.AlterField( + model_name='luxoptioncontact', + name='call_put', + field=models.IntegerField(choices=[(0, 'C'), (1, 'P')], default=0), + ), + ] diff --git a/app/trading/migrations/0020_auto_20220103_1517.py b/app/trading/migrations/0020_auto_20220103_1517.py new file mode 100644 index 0000000..73c4b5f --- /dev/null +++ b/app/trading/migrations/0020_auto_20220103_1517.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.7 on 2022-01-03 15:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0019_auto_20220103_1458'), + ] + + operations = [ + migrations.AddField( + model_name='luxoptionpurchase', + name='close_date', + field=models.DateTimeField(null=True), + ), + migrations.AddField( + model_name='luxoptionpurchase', + name='status', + field=models.IntegerField(choices=[(0, 'Open'), (1, 'Closed')], default=1), + ), + ] diff --git a/app/trading/models.py b/app/trading/models.py index f0f332c..212d196 100644 --- a/app/trading/models.py +++ b/app/trading/models.py @@ -205,6 +205,77 @@ class LuxOptionsTradeStatsManager(models.Manager): return self.filter(close_date__range=(start_date, end_date)).aggregate(Sum('pl')) +class LuxOptionPurchase(models.Model): + symbol = models.CharField(max_length=256) + open_date = models.DateTimeField(auto_now_add=True) + close_date = models.DateTimeField(null=True, blank=True) + pl = models.FloatField(null=True, blank=True) + STATUS = ( + (0, 'Open'), + (1, 'Closed'), + ) + status = models.IntegerField(choices=STATUS, default=1) + + class Meta: + ordering = ('-open_date',) + get_latest_by = 'open_date' + + def __str__(self): + return str(self.symbol) + + def get_contract_count(self): + return self.luxoptioncontact_set.count() + + @property + def stop_price(self): + return (self.contract_price*.75) + + @property + def total_invested(self): + return round(self.get_contract_count()*(self.luxoptioncontact_set.first().contract_open_price*100)) + + @property + def trade_risk(self): + return round(self.total_invested*.25) + + @property + def portfolio_risk(self): + return (self.trade_risk/10000)*100 + + @property + def sell_half_at(self): + return self.luxoptioncontact_set.first().contract_open_price*1.25 + + @property + def contract_price(self): + return self.luxoptioncontact_set.first().contract_open_price + + def save(self, *args, **kwargs): + if self.status == 1 and not self.close_date: + self.close_date = timezone.now() + if self.status == 1 and not self.pl: + pass + #self.pl = round((self.close_price*self.shares)-(self.entry_price*self.shares), 2) + super(LuxOptionPurchase, self).save() + + +class LuxOptionContact(models.Model): + symbol = models.CharField(max_length=256) + strike_price = models.FloatField() + expiration_date = models.DateField() + contract_open_price = models.FloatField() + contract_close_price = models.FloatField(null=True, blank=True) + CALL_PUT = ( + (0, 'Calls'), + (1, 'Puts'), + ) + call_put = models.IntegerField(choices=CALL_PUT, default=0) + options_purchase = models.ForeignKey(LuxOptionPurchase, null=True, on_delete=models.SET_NULL) + + def __str__(self): + return "%s - %s %s" %(self.symbol, round(self.strike_price), self.get_call_put_display()) + + class LuxOptionsTrade(models.Model): symbol = models.CharField(max_length=256) date = models.DateTimeField(auto_now_add=True) @@ -322,8 +393,6 @@ class LuxOptionsTrade(models.Model): super(LuxOptionsTrade, self).save() - - class TradeJrnl(models.Model): date = models.DateTimeField(auto_now_add=True) body_markdown = models.TextField(blank=True) diff --git a/app/trading/templates/trading/create_luxoptions_form.html b/app/trading/templates/trading/create_luxoptions_form.html new file mode 100644 index 0000000..5fff0ae --- /dev/null +++ b/app/trading/templates/trading/create_luxoptions_form.html @@ -0,0 +1,68 @@ +{% extends 'trading/base.html' %} +{% load typogrify_tags %} + {% block content %} +
{% csrf_token %} + {% for field in form %} +
+ {{ field.errors }} + {% if field.name == 'status' or field.name == 'call_put' %} + {{ field }} + {% else %} + {{ field.label_tag }} {{ field }} + {% endif %} + {% if field.help_text %} +

{{ field.help_text|safe }}

+ {% endif %} +
+{% endfor %} +
+
R/R:
+
% Portfolio:
+
Risk per contract:
+
Total Risk:
+
Total Invested:
+
+
+ +
+
+ {% endblock %} + + {% block js %} + + {% endblock %} diff --git a/app/trading/templates/trading/list.html b/app/trading/templates/trading/list.html index 390b64e..bf8104b 100644 --- a/app/trading/templates/trading/list.html +++ b/app/trading/templates/trading/list.html @@ -1,6 +1,55 @@ {% extends 'trading/base.html' %} {% block content %} +

WON Options Trades

+ + + + + + + + + + + + + + + {% for object in luxoptions_purchases %} + + + + + + + + + + {%if object.status == 2%}{% endif %} + + {% endfor %} +
SymbolOpen DateContractTotal Invested$ Trade Risk% Portfolio Risk25% profit atStopNotes
{{object.symbol}}{{object.open_date|date:"Y-m-d"}} +
+ {% for contract in object.luxoptioncontact_set.all %}{% if forloop.first%} + {{object.get_contract_count}} + {{contract.expiration_date|date:"Mj"}} + ${{contract.strike_price|floatformat:0}} {{contract.get_call_put_display|title}} + @ ${{object.contract_price|floatformat:2}} +{%endif%} +{%endfor%} +
+ +
${{object.total_invested}}${{object.trade_risk}}{{object.portfolio_risk}}%${{object.sell_half_at|floatformat:2}}${{object.stop_price|floatformat:2}} + {% if object.notes %} +
+ {{object.notes_html|safe}} +
+ {% endif %} +
+ +
+ {% comment %}

Options Trades

@@ -66,11 +115,12 @@
+ {% endcomment %} -

Stock Trades

+

Open Stock Trades

@@ -146,7 +196,7 @@ -

Trade History

+

Stock History

@@ -336,6 +386,29 @@ for (var i = 0; i < elements.length; i++) { elements[i].addEventListener('input', getCurrentProfit, false); } + var els = document.getElementsByClassName("data_calc"); + var getData = function() { + var options = this.getAttribute("data-options"); + var eprice = this.getAttribute("data-eprice"); + var sprice = this.getAttribute("data-sprice"); + var shares = this.getAttribute("data-shares"); + var contract_price = this.getAttribute("data-cprice"); + var number_contracts = this.getAttribute("data-ncontracts"); + var total = this.getAttribute("data-total"); + var delta = this.getAttribute("data-delta"); + var cprice = this.value; + if (options) { + var profit = ((cprice*number_contracts)*100)-((contract_price*number_contracts)*100); + this.nextSibling.innerHTML = "$"+profit.toFixed(2); + } else { + var profit = (cprice*shares)-(shares*eprice) + this.nextSibling.innerHTML = "$"+profit.toFixed(2); + } + }; + + for (var i = 0; i < elements.length; i++) { + els[i].addEventListener('input', getData, false); + } {% endblock %} diff --git a/app/trading/urls.py b/app/trading/urls.py index 2a70e13..6bb486a 100644 --- a/app/trading/urls.py +++ b/app/trading/urls.py @@ -10,9 +10,14 @@ urlpatterns = [ views.TradeModelFormView.as_view(), name='testtrade' ), + #path( + # 'options-calc', + # views.OptionsModelFormView.as_view(), + # name='testoptions' + #), path( 'options-calc', - views.OptionsModelFormView.as_view(), + views.LuxOptionPurchaseCreateView.as_view(), name='testoptions' ), path( diff --git a/app/trading/views.py b/app/trading/views.py index 2fb960a..686a344 100644 --- a/app/trading/views.py +++ b/app/trading/views.py @@ -1,8 +1,10 @@ from datetime import datetime +from django.http import HttpResponseRedirect from django.views.generic.edit import CreateView, UpdateView from utils.views import PaginatedListView -from .models import LuxTrade, LuxOptionsTrade +from .models import LuxTrade, LuxOptionsTrade, LuxOptionContact, LuxOptionPurchase +from .forms import LuxOptionsForm class LuxTradeListView(PaginatedListView): @@ -21,6 +23,7 @@ class LuxTradeListView(PaginatedListView): context['options_monthly_pl'] = LuxOptionsTrade.stats.get_month_pl() context['options_year_pl'] = LuxOptionsTrade.stats.get_year_pl() context['month'] = datetime.now().strftime('%h') + context['luxoptions_purchases'] = LuxOptionPurchase.objects.filter(status=0) return context def get_queryset(self): @@ -82,3 +85,37 @@ class OptionsModelFormView(CreateView): ] success_url = '/trading/' template_name = 'trading/create_options_form.html' + + +class LuxOptionPurchaseCreateView(CreateView): + model = LuxOptionContact + fields = ['symbol'] + success_url = '/trading/' + template_name = 'trading/create_luxoptions_form.html' + + + def get_context_data(self, **kwargs): + context = super(LuxOptionPurchaseFormView, self).get_context_data(**kwargs) + context = {'form': LuxOptionsForm()} + return context + + def post(self, request, *args, **kwargs): + form = LuxOptionsForm(request.POST) + if form.is_valid(): + i = 0 + p = LuxOptionPurchase.objects.create( + symbol = form.cleaned_data['symbol'], + ) + print(form.cleaned_data['contracts']) + while i < int(form.cleaned_data['contracts']): + c = LuxOptionContact.objects.create( + symbol = form.cleaned_data['symbol'], + strike_price = form.cleaned_data['strike_price'], + expiration_date = form.cleaned_data['expiration_date'], + contract_open_price = form.cleaned_data['contract_open_price'], + call_put = form.cleaned_data['call_put'], + options_purchase = p + ) + i = i+1 + return HttpResponseRedirect('/trading/') + return render(request, 'trading/create_luxoptions_form.html', {'form': form}) -- cgit v1.2.3-70-g09d2