summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlxf <sng@luxagraf.net>2022-01-03 17:02:50 -0500
committerlxf <sng@luxagraf.net>2022-01-03 17:02:50 -0500
commit617b485a0147266d93ec7db22afd2cd9055b0c09 (patch)
tree93462a0cf8c93c211c3db7223df48f94f94f3b96
parent1123d655bd6708fba056d9800af61e9f2e8bd6eb (diff)
trad: new options tracker that works better for WON system
-rw-r--r--app/trading/admin.py25
-rw-r--r--app/trading/forms.py25
-rw-r--r--app/trading/migrations/0017_luxoptioncontact_luxoptionpurchase.py34
-rw-r--r--app/trading/migrations/0018_luxoptionpurchase_pl.py18
-rw-r--r--app/trading/migrations/0019_auto_20220103_1458.py32
-rw-r--r--app/trading/migrations/0020_auto_20220103_1517.py23
-rw-r--r--app/trading/models.py73
-rw-r--r--app/trading/templates/trading/create_luxoptions_form.html68
-rw-r--r--app/trading/templates/trading/list.html77
-rw-r--r--app/trading/urls.py7
-rw-r--r--app/trading/views.py39
11 files changed, 414 insertions, 7 deletions
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 %}
+ <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 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 %}
+ <h3>WON Options Trades</h3>
+ <table class="options">
+ <thead>
+ <tr>
+ <th>Symbol</th>
+ <th>Open Date</th>
+ <th>Contract</th>
+ <th>Total Invested</th>
+ <th>$ Trade Risk</th>
+ <th>% Portfolio Risk</th>
+ <th>25% profit at</th>
+ <th>Stop</th>
+ <th>Notes</th>
+ </tr>
+ </thead>
+ {% for object in luxoptions_purchases %}
+ <tr>
+ <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.open_date|date:"Y-m-d"}}</a></td>
+ <td>
+ <div class="data_calc">
+ {% for contract in object.luxoptioncontact_set.all %}{% if forloop.first%}
+ <span id="contract" data-num-contracts="{{object.get_contract_count}}">{{object.get_contract_count}}</span>
+ <span>{{contract.expiration_date|date:"Mj"}}</span>
+ <span>${{contract.strike_price|floatformat:0}} {{contract.get_call_put_display|title}}</span>
+ <span>@ ${{object.contract_price|floatformat:2}}</span>
+{%endif%}
+{%endfor%}
+ </div>
+
+ <td>${{object.total_invested}}</td>
+ <td>${{object.trade_risk}}</td>
+ <td>{{object.portfolio_risk}}%</td>
+ <td>${{object.sell_half_at|floatformat:2}}</td>
+ <td>${{object.stop_price|floatformat:2}}</td>
+ <td class="notes">
+ {% if object.notes %}
+ <div class="wrapper">
+ {{object.notes_html|safe}}
+ </div>
+ {% endif %}
+ </td>
+ {%if object.status == 2%}<td>
+ <a href="https://live.luxagraf.net/admin/trading/luxoptionstrade/{{object.pk}}/delete/">&#8709;</a>
+ </td>{% endif %}
+ </tr>
+ {% endfor %}
+ </table>
+ {% comment %}
<h3>Options Trades</h3>
<table class="options">
<thead>
@@ -66,11 +115,12 @@
</table>
+ {% endcomment %}
- <h3>Stock Trades</h3>
+ <h3>Open Stock Trades</h3>
<table>
<thead>
<tr>
@@ -146,7 +196,7 @@
- <h3>Trade History</h3>
+ <h3>Stock History</h3>
<table>
<thead>
<tr>
@@ -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);
+ }
</script>
{% 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})