diff options
author | luxagraf <sng@luxagraf.net> | 2021-07-20 21:49:09 -0400 |
---|---|---|
committer | luxagraf <sng@luxagraf.net> | 2021-07-20 21:49:09 -0400 |
commit | 4e64ac45ad91b79b9fc0cd80abab51fc50e8be76 (patch) | |
tree | 0f84543e0d7eb538dbd47ca45cd3b340704a5e7f /app/trading | |
parent | 538481a01b39a73f1590cf9910b3c884270bd204 (diff) |
trad: added options tracking and calculator
Diffstat (limited to 'app/trading')
-rw-r--r-- | app/trading/migrations/0011_auto_20210720_2039.py | 49 | ||||
-rw-r--r-- | app/trading/migrations/0012_alter_luxoptionstrade_call_put.py | 18 | ||||
-rw-r--r-- | app/trading/migrations/0013_alter_luxoptionstrade_fees.py | 18 | ||||
-rw-r--r-- | app/trading/models.py | 85 | ||||
-rw-r--r-- | app/trading/templates/trading/base.html | 2 | ||||
-rw-r--r-- | app/trading/templates/trading/create_form.html | 4 | ||||
-rw-r--r-- | app/trading/templates/trading/create_options_form.html | 68 | ||||
-rw-r--r-- | app/trading/templates/trading/list.html | 54 | ||||
-rw-r--r-- | app/trading/templates/trading/update_options_form.html | 68 | ||||
-rw-r--r-- | app/trading/urls.py | 12 | ||||
-rw-r--r-- | app/trading/views.py | 79 |
11 files changed, 429 insertions, 28 deletions
diff --git a/app/trading/migrations/0011_auto_20210720_2039.py b/app/trading/migrations/0011_auto_20210720_2039.py new file mode 100644 index 0000000..8ff6693 --- /dev/null +++ b/app/trading/migrations/0011_auto_20210720_2039.py @@ -0,0 +1,49 @@ +# Generated by Django 3.2.5 on 2021-07-20 20:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0010_auto_20210716_1344'), + ] + + operations = [ + migrations.CreateModel( + name='LuxOptionsTrade', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('symbol', models.CharField(max_length=256)), + ('date', models.DateTimeField(auto_now_add=True)), + ('close_date', models.DateTimeField(blank=True, null=True)), + ('open_date', models.DateTimeField(blank=True, null=True)), + ('entry_price', models.FloatField()), + ('stop_price', models.FloatField()), + ('target_price', models.FloatField()), + ('strike_price', models.FloatField()), + ('call_put', models.CharField(choices=[(0, 'Call'), (1, 'Put')], default=0, max_length=4)), + ('contract_price', models.FloatField()), + ('number_contracts', models.FloatField()), + ('delta', models.FloatField()), + ('expiration_date', models.DateField()), + ('fees', models.FloatField()), + ('status', models.IntegerField(choices=[(0, 'Open'), (1, 'Closed'), (2, 'Watching')], default=2)), + ('notes', models.TextField(blank=True, null=True)), + ('pl', models.FloatField(null=True)), + ], + options={ + 'ordering': ('-open_date',), + 'get_latest_by': 'open_date', + }, + ), + migrations.AlterModelOptions( + name='luxtrade', + options={'get_latest_by': 'open_date', 'ordering': ('-open_date',)}, + ), + migrations.AlterModelManagers( + name='luxtrade', + managers=[ + ], + ), + ] diff --git a/app/trading/migrations/0012_alter_luxoptionstrade_call_put.py b/app/trading/migrations/0012_alter_luxoptionstrade_call_put.py new file mode 100644 index 0000000..c8068a9 --- /dev/null +++ b/app/trading/migrations/0012_alter_luxoptionstrade_call_put.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.5 on 2021-07-20 20:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0011_auto_20210720_2039'), + ] + + operations = [ + migrations.AlterField( + model_name='luxoptionstrade', + name='call_put', + field=models.IntegerField(choices=[(0, 'Call'), (1, 'Put')], default=2), + ), + ] diff --git a/app/trading/migrations/0013_alter_luxoptionstrade_fees.py b/app/trading/migrations/0013_alter_luxoptionstrade_fees.py new file mode 100644 index 0000000..1eccf8c --- /dev/null +++ b/app/trading/migrations/0013_alter_luxoptionstrade_fees.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.5 on 2021-07-20 20:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0012_alter_luxoptionstrade_call_put'), + ] + + operations = [ + migrations.AlterField( + model_name='luxoptionstrade', + name='fees', + field=models.FloatField(blank=True), + ), + ] diff --git a/app/trading/models.py b/app/trading/models.py index 75476fd..551a4de 100644 --- a/app/trading/models.py +++ b/app/trading/models.py @@ -127,6 +127,10 @@ class LuxTrade(models.Model): is_wanderer = models.BooleanField(default=True) pl = models.FloatField(null=True) + class Meta: + ordering = ('-open_date',) + get_latest_by = 'open_date' + def __str__(self): return str(self.symbol) @@ -179,6 +183,87 @@ class LuxTrade(models.Model): self.pl = round((self.close_price*self.shares)-(self.entry_price*self.shares), 2) super(LuxTrade, self).save() + +class LuxOptionsTrade(models.Model): + symbol = models.CharField(max_length=256) + date = models.DateTimeField(auto_now_add=True) + close_date = models.DateTimeField(null=True, blank=True) + open_date = models.DateTimeField(null=True, blank=True) + entry_price = models.FloatField() + stop_price = models.FloatField() + target_price = models.FloatField() + strike_price = models.FloatField() + CALL_PUT = ( + (0, 'Call'), + (1, 'Put'), + ) + call_put = models.IntegerField(choices=CALL_PUT, default=2) + contract_price = models.FloatField() + number_contracts = models.FloatField() + delta = models.FloatField() + expiration_date = models.DateField() + + fees = models.FloatField(blank=True) + STATUS = ( + (0, 'Open'), + (1, 'Closed'), + (2, 'Watching'), + ) + status = models.IntegerField(choices=STATUS, default=2) + notes = models.TextField(null=True, blank=True) + pl = models.FloatField(null=True) + + class Meta: + ordering = ('-open_date',) + get_latest_by = 'open_date' + + def __str__(self): + return str(self.symbol) + + def get_absolute_url(self): + return reverse('luxtrade:optiondetail', kwargs={"pk": self.pk}) + + @property + def risk_per_contract(self): + return round(((self.entry_price-self.stop_price)*self.delta/self.contract_price)*self.contract_price*100, 2) + + @property + def risk_total(self): + return round(self.risk_per_contract*self.number_contracts, 2) + + @property + def risk_reward(self): + return round((self.target_price-self.entry_price)/(self.entry_price-self.stop_price), 2); + + @property + def amount_invested(self): + return round(((self.contract_price * self.number_contracts)*100)+self.fees, 2) + + @property + def profit_goal(self): + if self.call_put == 0: + return round((((self.target_price-(self.strike_price+self.contract_price))*100)*self.number_contracts)-self.fees, 3) + + @property + def days_until_expiration(self): + td = self.expiration_date - datetime.date.today() + return td.days + + def save(self, *args, **kwargs): + if self.status == 0 and not self.open_date: + self.open_date = timezone.now() + if self.number_contracts < 10: + self.fees = self.number_contracts + (.14*self.number_contracts) + else: + self.fees = 10 + (.14*self.number_contracts) + if self.status == 1 and not self.close_date: + self.close_date = timezone.now() + if self.status == 1 and not self.pl: + self.pl = 0 #round((self.close_price*self.shares)-(self.entry_price*self.shares), 2) + super(LuxOptionsTrade, self).save() + + + class TradeJrnl(models.Model): date = models.DateTimeField(auto_now_add=True) diff --git a/app/trading/templates/trading/base.html b/app/trading/templates/trading/base.html index 86023ba..230a661 100644 --- a/app/trading/templates/trading/base.html +++ b/app/trading/templates/trading/base.html @@ -15,7 +15,7 @@ <nav> <span class="nav-item"><a href="{% url 'luxtrade:list' %}">Home</a></span> <span class="nav-item"><a href="{% url 'luxtrade:testtrade' %}">Test Trade</a></span> - <span class="nav-item"><a href="{% url 'luxtrade:testtrade' %}">Test Options</a></span> + <span class="nav-item"><a href="{% url 'luxtrade:testoptions' %}">Test Options</a></span> <span class="nav-item"><a href="https://wandererfinancial.com/{% now 'n-j-y'%}-todays-market/" target="_blank">Wanderer</a></span> <span class="nav-item"><a href="https://www.tradingview.com/chart/1a1NjVtp/" target="_blank">Trading View</a></span> <span class="nav-item"><a href="https://client.schwab.com/Areas/Accounts/Positions" target="_blank">Schwab Positions</a></span> diff --git a/app/trading/templates/trading/create_form.html b/app/trading/templates/trading/create_form.html index 19efea8..890c9e2 100644 --- a/app/trading/templates/trading/create_form.html +++ b/app/trading/templates/trading/create_form.html @@ -1,7 +1,7 @@ {% extends 'trading/base.html' %} {% load typogrify_tags %} {% block content %} - <form id="id_form" action="/trading/model" method="post" class="big">{% csrf_token %} + <form id="id_form" action="{% url 'luxtrade:testtrade' %}" method="post" class="big">{% csrf_token %} {% for field in form %} <fieldset> {{ field.errors }} @@ -39,7 +39,7 @@ function calcPercentPortfolio() { var pp = (entry_price*shares)/20000; var risk_dollars = (entry_price-stop_price)*shares; var goal = (target_price*shares)-(entry_price*shares); - var rr = (entry_price-stop_price)/(target_price-entry_price); + var rr = (target_price-entry_price)/(entry_price-stop_price); var total = entry_price*shares var percent_goal = (goal/total)*100 document.getElementById("id_p_portfolio").innerText = (pp*100).toFixed(2); diff --git a/app/trading/templates/trading/create_options_form.html b/app/trading/templates/trading/create_options_form.html new file mode 100644 index 0000000..5fff0ae --- /dev/null +++ b/app/trading/templates/trading/create_options_form.html @@ -0,0 +1,68 @@ +{% extends 'trading/base.html' %} +{% load typogrify_tags %} + {% block content %} + <form id="id_form" action="{% url 'luxtrade:testoptions' %}" method="post" class="big">{% csrf_token %} + {% for field in form %} + <fieldset> + {{ field.errors }} + {% if field.name == 'status' or field.name == 'call_put' %} + <label class="hide" for="id_status">Status:</label>{{ field }} + {% else %} + {{ field.label_tag }} {{ field }} + {% endif %} + {% if field.help_text %} + <p class="help">{{ field.help_text|safe }}</p> + {% endif %} + </fieldset> +{% endfor %} + <dl> + <dt>R/R: </dt><dd id="id_rr"></dd> + <dt>% Portfolio: </dt><dd id="id_p_portfolio"></dd> + <dt>Risk per contract: </dt><dd id="id_risk_contract"></dd> + <dt>Total Risk: </dt><dd id="id_risk_total"></dd> + <dt>Total Invested: </dt><dd id="id_total"></dd> + </dl> + <div class="flex"> + <input type="submit" name="post" class="btn" value="record purchase"/> + </div> + </form> + {% endblock %} + + {% block js %} +<script> +function calcPercentPortfolio() { + var entry_price = document.getElementById("id_entry_price").value; + var stop_price = document.getElementById("id_stop_price").value; + var target_price = document.getElementById("id_target_price").value; + var contract_price = document.getElementById("id_contract_price").value; + var number_contracts = document.getElementById("id_number_contracts").value; + var delta = document.getElementById("id_delta").value; + var pp = (contract_price*number_contracts)*100/10000; + var total = (contract_price*number_contracts)*100; + var rr = (target_price-entry_price)/(entry_price-stop_price); + var risk_per = ((entry_price-stop_price)*delta/contract_price)*contract_price*100 + var total_risk = (risk_per*number_contracts); + document.getElementById("id_p_portfolio").innerText = (pp*100).toFixed(2); + document.getElementById("id_risk_contract").innerText = "$"+risk_per.toFixed(2); + document.getElementById("id_risk_total").innerText = "$"+total_risk.toFixed(2); + document.getElementById("id_rr").innerText = rr.toFixed(2); + document.getElementById("id_total").innerText = "$"+total.toFixed(2); +} +id_form.addEventListener("input", function (e) { + calcPercentPortfolio(); +}); +var form = document.getElementById('id_form'); +function processForm(e) { + if (e.preventDefault) e.preventDefault(); + if(!confirm("Do you really want to do this?")) { + return false; + } + form.submit(); +} +if (form.attachEvent) { + form.attachEvent("submit", processForm); +} else { + form.addEventListener("submit", processForm); +} +</script> + {% endblock %} diff --git a/app/trading/templates/trading/list.html b/app/trading/templates/trading/list.html index b5b731a..ac735e3 100644 --- a/app/trading/templates/trading/list.html +++ b/app/trading/templates/trading/list.html @@ -74,6 +74,60 @@ </tr> {% endfor %} </table> + + <h3>Current Options Trades</h3> + <table> + <thead> + <tr> + <th>Symbol</th> + <th>Open Date</th> + <th>Details</th> + <th>Entry Price</th> + <th>Stop</th> + <th>Target</th> + <th>Delta</th> + <th>Contract $</th> + <th># Contract</th> + <th>Total Invested</th> + <th>Risk per</th> + <th>Risk Total</th> + <th>Profit Goal</th> + <th>Risk/Reward</th> + <th>Price Calc</th> + <th>Notes</th> + </tr> + </thead> + {% for object in options_trades %} + <tr {%if object.is_wanderer %}class="wanderer-trade"{% endif %}> + <td><a href="https://www.tradingview.com/chart/?symbol={{object.symbol}}" target="_blank">{{object.symbol}}</a></td> + <td><a href="{{object.get_absolute_url}}">{{object.date|date:"m-d-Y"}}</a></td> + <td> + <div class="{{object.call_put}}"> + <span>{{object.expiration_date|date:"M j"}}</span> + <span>{{object.days_until_expiration}}</span> + <span>{{object.get_call_put_display|first}}</span> + <span>${{object.strike_price}}</span> + </div> + <td>${{object.entry_price}}</td> + <td>${{object.stop_price}}</td> + <td>${{object.target_price}}</td> + <td>{{object.delta}}</td> + <td>${{object.contract_price}}</td> + <td>{{object.number_contracts}}</td> + <td>${{object.amount_invested}}</td> + <td>${{object.risk_per_contract}}</td> + <td>${{object.risk_total}}</td> + <td>${{object.profit_goal}}</td> + <td>{{object.risk_reward}}</td> + <td><input class="close_price_calc" id="id_close_price_{{forloop.counter}}"> <span id=profit"></span></td> + <td class="notes">{{object.notes}}</td> + </tr> + {% endfor %} + </table> + + + + <h3>Trade History</h3> <table> <thead> diff --git a/app/trading/templates/trading/update_options_form.html b/app/trading/templates/trading/update_options_form.html new file mode 100644 index 0000000..a352cb3 --- /dev/null +++ b/app/trading/templates/trading/update_options_form.html @@ -0,0 +1,68 @@ +{% extends 'trading/base.html' %} +{% load typogrify_tags %} + {% block content %} + <form id="id_form" action="" method="post" class="big">{% csrf_token %} + {% for field in form %} + <fieldset> + {{ field.errors }} + {% if field.name == 'status' or field.name == 'call_put' %} + <label class="hide" for="id_status">Status:</label>{{ field }} + {% else %} + {{ field.label_tag }} {{ field }} + {% endif %} + {% if field.help_text %} + <p class="help">{{ field.help_text|safe }}</p> + {% endif %} + </fieldset> +{% endfor %} + <dl> + <dt>R/R: </dt><dd id="id_rr"></dd> + <dt>% Portfolio: </dt><dd id="id_p_portfolio"></dd> + <dt>Risk per contract: </dt><dd id="id_risk_contract"></dd> + <dt>Total Risk: </dt><dd id="id_risk_total"></dd> + <dt>Total Invested: </dt><dd id="id_total"></dd> + </dl> + <div class="flex"> + <input type="submit" name="post" class="btn" value="record purchase"/> + </div> + </form> + {% endblock %} + + {% block js %} +<script> +function calcPercentPortfolio() { + var entry_price = document.getElementById("id_entry_price").value; + var stop_price = document.getElementById("id_stop_price").value; + var target_price = document.getElementById("id_target_price").value; + var contract_price = document.getElementById("id_contract_price").value; + var number_contracts = document.getElementById("id_number_contracts").value; + var delta = document.getElementById("id_delta").value; + var pp = (contract_price*number_contracts)*100/10000; + var total = (contract_price*number_contracts)*100; + var rr = (target_price-entry_price)/(entry_price-stop_price); + var risk_per = ((entry_price-stop_price)*delta/contract_price)*contract_price*100 + var total_risk = (risk_per*number_contracts); + document.getElementById("id_p_portfolio").innerText = (pp*100).toFixed(2); + document.getElementById("id_risk_contract").innerText = "$"+risk_per.toFixed(2); + document.getElementById("id_risk_total").innerText = "$"+total_risk.toFixed(2); + document.getElementById("id_rr").innerText = rr.toFixed(2); + document.getElementById("id_total").innerText = "$"+total.toFixed(2); +} +id_form.addEventListener("input", function (e) { + calcPercentPortfolio(); +}); +var form = document.getElementById('id_form'); +function processForm(e) { + if (e.preventDefault) e.preventDefault(); + if(!confirm("Do you really want to do this?")) { + return false; + } + form.submit(); +} +if (form.attachEvent) { + form.attachEvent("submit", processForm); +} else { + form.addEventListener("submit", processForm); +} +</script> + {% endblock %} diff --git a/app/trading/urls.py b/app/trading/urls.py index 986339a..2a70e13 100644 --- a/app/trading/urls.py +++ b/app/trading/urls.py @@ -6,11 +6,21 @@ app_name = "luxtrade" urlpatterns = [ path( - 'model', + 'trade-calc', views.TradeModelFormView.as_view(), name='testtrade' ), path( + 'options-calc', + views.OptionsModelFormView.as_view(), + name='testoptions' + ), + path( + 'trade/options/<pk>', + views.LuxOptionsTradeDetailView.as_view(), + name='optiondetail' + ), + path( 'trade/<pk>', views.LuxTradeDetailView.as_view(), name='detail' diff --git a/app/trading/views.py b/app/trading/views.py index 4d33497..aece7b2 100644 --- a/app/trading/views.py +++ b/app/trading/views.py @@ -1,32 +1,10 @@ from datetime import datetime -from django.shortcuts import render -from django.views.generic.edit import FormView -from django.views.generic.edit import CreateView, DeleteView, UpdateView +from django.views.generic.edit import CreateView, UpdateView from utils.views import PaginatedListView -from .models import OptionsTrade, LuxTrade +from .models import LuxTrade, LuxOptionsTrade -class OptionsTradeResultsView(PaginatedListView): - model = OptionsTrade - - def get_context_data(self, **kwargs): - # Call the base implementation first to get a context - context = super(OptionsTradeResultsView, self).get_context_data(**kwargs) - return context - -class TradeModelFormView(CreateView): - model = LuxTrade - fields = ['symbol', 'status', 'entry_price', 'stop_price', 'target_price', 'shares', 'is_wanderer'] - success_url = '/trading/' - template_name = 'trading/create_form.html' - -class LuxTradeDetailView(UpdateView): - model = LuxTrade - fields = ['symbol', 'status', 'entry_price', 'stop_price', 'target_price', 'shares', 'close_price', 'notes', 'is_wanderer'] - template_name = 'trading/update_form.html' - success_url = '/trading/' - class LuxTradeListView(PaginatedListView): model = LuxTrade template_name = 'trading/list.html' @@ -36,6 +14,7 @@ class LuxTradeListView(PaginatedListView): context = super(LuxTradeListView, self).get_context_data(**kwargs) context['open_trades'] = LuxTrade.objects.filter(status=0) context['watch_trades'] = LuxTrade.objects.filter(status=2) + context['options_trades'] = LuxOptionsTrade.objects.filter(status=2) context['monthly_pl'] = LuxTrade.stats.get_month_pl() context['month'] = datetime.now().strftime('%h') return context @@ -43,3 +22,55 @@ class LuxTradeListView(PaginatedListView): def get_queryset(self): queryset = super(LuxTradeListView, self).get_queryset() return queryset.filter(status=1) + + +class LuxTradeDetailView(UpdateView): + model = LuxTrade + fields = ['symbol', 'status', 'entry_price', 'stop_price', 'target_price', 'shares', 'close_price', 'notes', 'is_wanderer'] + template_name = 'trading/update_form.html' + success_url = '/trading/' + + +class TradeModelFormView(CreateView): + model = LuxTrade + fields = ['symbol', 'status', 'entry_price', 'stop_price', 'target_price', 'shares', 'is_wanderer'] + success_url = '/trading/' + template_name = 'trading/create_form.html' + + +class LuxOptionsTradeDetailView(UpdateView): + model = LuxOptionsTrade + fields = [ + 'symbol', + 'status', + 'entry_price', + 'stop_price', + 'target_price', + 'call_put', + 'expiration_date', + 'strike_price', + 'contract_price', + 'number_contracts', + 'delta' + ] + template_name = 'trading/update_options_form.html' + success_url = '/trading/' + + +class OptionsModelFormView(CreateView): + model = LuxOptionsTrade + fields = [ + 'symbol', + 'status', + 'entry_price', + 'stop_price', + 'target_price', + 'call_put', + 'expiration_date', + 'strike_price', + 'contract_price', + 'number_contracts', + 'delta' + ] + success_url = '/trading/' + template_name = 'trading/create_options_form.html' |