summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorluxagraf <sng@luxagraf.net>2021-07-20 21:49:09 -0400
committerluxagraf <sng@luxagraf.net>2021-07-20 21:49:09 -0400
commit4e64ac45ad91b79b9fc0cd80abab51fc50e8be76 (patch)
tree0f84543e0d7eb538dbd47ca45cd3b340704a5e7f /app
parent538481a01b39a73f1590cf9910b3c884270bd204 (diff)
trad: added options tracking and calculator
Diffstat (limited to 'app')
-rw-r--r--app/trading/migrations/0011_auto_20210720_2039.py49
-rw-r--r--app/trading/migrations/0012_alter_luxoptionstrade_call_put.py18
-rw-r--r--app/trading/migrations/0013_alter_luxoptionstrade_fees.py18
-rw-r--r--app/trading/models.py85
-rw-r--r--app/trading/templates/trading/base.html2
-rw-r--r--app/trading/templates/trading/create_form.html4
-rw-r--r--app/trading/templates/trading/create_options_form.html68
-rw-r--r--app/trading/templates/trading/list.html54
-rw-r--r--app/trading/templates/trading/update_options_form.html68
-rw-r--r--app/trading/urls.py12
-rw-r--r--app/trading/views.py79
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'