From 4e64ac45ad91b79b9fc0cd80abab51fc50e8be76 Mon Sep 17 00:00:00 2001
From: luxagraf <sng@luxagraf.net>
Date: Tue, 20 Jul 2021 21:49:09 -0400
Subject: trad: added options tracking and calculator

---
 app/trading/migrations/0011_auto_20210720_2039.py  | 49 +++++++++++++
 .../0012_alter_luxoptionstrade_call_put.py         | 18 +++++
 .../migrations/0013_alter_luxoptionstrade_fees.py  | 18 +++++
 app/trading/models.py                              | 85 ++++++++++++++++++++++
 app/trading/templates/trading/base.html            |  2 +-
 app/trading/templates/trading/create_form.html     |  4 +-
 .../templates/trading/create_options_form.html     | 68 +++++++++++++++++
 app/trading/templates/trading/list.html            | 54 ++++++++++++++
 .../templates/trading/update_options_form.html     | 68 +++++++++++++++++
 app/trading/urls.py                                | 12 ++-
 app/trading/views.py                               | 79 ++++++++++++++------
 11 files changed, 429 insertions(+), 28 deletions(-)
 create mode 100644 app/trading/migrations/0011_auto_20210720_2039.py
 create mode 100644 app/trading/migrations/0012_alter_luxoptionstrade_call_put.py
 create mode 100644 app/trading/migrations/0013_alter_luxoptionstrade_fees.py
 create mode 100644 app/trading/templates/trading/create_options_form.html
 create mode 100644 app/trading/templates/trading/update_options_form.html

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,10 +6,20 @@ 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(),
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'
-- 
cgit v1.2.3-70-g09d2