diff options
author | luxagraf <sng@luxagraf.net> | 2021-07-14 09:31:15 -0400 |
---|---|---|
committer | luxagraf <sng@luxagraf.net> | 2021-07-14 09:31:15 -0400 |
commit | 39086b58106ff6181d029e0ef845593605821873 (patch) | |
tree | b6652d1b74c1187eafbd0fbd74a3f00f4f1c9c58 /app | |
parent | c2c0421207694aa5eb148f05a57440716ae2dc8d (diff) |
trading: added trading to toolset
Diffstat (limited to 'app')
-rw-r--r-- | app/trading/admin.py | 57 | ||||
-rw-r--r-- | app/trading/importer.py | 33 | ||||
-rw-r--r-- | app/trading/migrations/0001_initial.py | 65 | ||||
-rw-r--r-- | app/trading/migrations/0002_remove_luxtrademodel_percent_portfolio.py | 17 | ||||
-rw-r--r-- | app/trading/migrations/0003_luxtrademodel_status.py | 18 | ||||
-rw-r--r-- | app/trading/migrations/0004_rename_luxtrademodel_luxtrade.py | 17 | ||||
-rw-r--r-- | app/trading/migrations/0005_auto_20210714_0747.py | 28 | ||||
-rw-r--r-- | app/trading/migrations/0006_luxtrade_close_price.py | 18 | ||||
-rw-r--r-- | app/trading/migrations/0007_alter_luxtrade_status.py | 18 | ||||
-rw-r--r-- | app/trading/migrations/__init__.py | 0 | ||||
-rw-r--r-- | app/trading/models.py | 174 | ||||
-rw-r--r-- | app/trading/templates/products/snippet.html | 41 | ||||
-rw-r--r-- | app/trading/templates/trading/base.html | 30 | ||||
-rw-r--r-- | app/trading/templates/trading/create_form.html | 67 | ||||
-rw-r--r-- | app/trading/templates/trading/list.html | 107 | ||||
-rw-r--r-- | app/trading/templates/trading/update_form.html | 20 | ||||
-rw-r--r-- | app/trading/urls.py | 24 | ||||
-rw-r--r-- | app/trading/views.py | 43 |
18 files changed, 777 insertions, 0 deletions
diff --git a/app/trading/admin.py b/app/trading/admin.py new file mode 100644 index 0000000..f95a272 --- /dev/null +++ b/app/trading/admin.py @@ -0,0 +1,57 @@ +from django.contrib import admin +from markdownx.widgets import AdminMarkdownxWidget +from django.db import models +from .models import TradeJrnl, OptionsTrade +from utils.widgets import AdminImageWidget, LGEntryForm + + +@admin.register(TradeJrnl) +class TradingJrnlAdmin(admin.ModelAdmin): + form = LGEntryForm + formfield_overrides = { + models.TextField: {'widget': AdminMarkdownxWidget}, + } + class Media: + js = ('image-loader.js', 'next-prev-links.js') + css = { + "all": ("my_styles.css",) + } + + +@admin.register(OptionsTrade) +class OptionsTradeAdmin(admin.ModelAdmin): + list_display = ('symbol', 'date', 'buy_sell', 'open_close', 'quantity') + list_filter = ['buy_sell'] + class Media: + js = ('image-loader.js', 'next-prev-links.js') + css = { + "all": ("my_styles.css",) + } + +""" + date = models.DateTimeField() + symbol = models.ForeignKey(Ticker, null=True, on_delete=models.SET_NULL) + TRANSACTION_CODE = ( + ('Trade', 'Trade'), + ) + transaction_code = models.CharField(choices=TRANSACTION_CODE, max_length=25) + TRANSACTION_SUBCODE = ( + ('Buy to Open', 'Buy to Open'), + ('Sell to Open', 'Sell to Open'), + ('Sell to Close', 'Sell to Close'), + ) + transaction_subcode = models.CharField(choices=TRANSACTION_SUBCODE, max_length=25) + open_close = models.CharField(choices=OPEN_CLOSE, max_length=5) + quantity = models.FloatField() + expiration_date = models.DateTimeField() + strike = models.FloatField() + CALL_PUT = ( + ('C', 'Call'), + ('P', 'Put'), + ) + call_put = models.CharField(choices=CALL_PUT, max_length=4) + price = models.FloatField() + fees = models.FloatField() + amount = models.FloatField() + description = models.TextField(blank=True) +""" diff --git a/app/trading/importer.py b/app/trading/importer.py new file mode 100644 index 0000000..a975ff5 --- /dev/null +++ b/app/trading/importer.py @@ -0,0 +1,33 @@ +import csv +from datetime import datetime + +from .models import Ticker + +with open(FILENAME, 'r' ) as theFile: + reader = csv.DictReader(theFile) + for line in reader: + dt = datetime.strptime(line['Date/Time'], '%m/%d/%Y %H:%M %p') + expiration_date = datetime.strptime(line['Expiration Date'], '%m/%d/%Y') + ticker, created = Ticker.objects.get_or_create( + symbol=line['Symbol'] + ) + print(ticker) + t, created = OptionsTrade.objects.get_or_create( + date=dt, + transaction_code=line['Transaction Code'], + transaction_subcode=line['Transaction Subcode'], + symbol=ticker, + buy_sell=line['Buy/Sell'], + open_close=line['Open/Close'], + quantity=int(line['Quantity']), + expiration_date = expiration_date, + strike = line['Strike'], + call_put = line['Call/Put'], + price = line['Price'], + fees = line['Fees'], + amount = line['Amount'], + description = line['Description'] + ) + if created: + print(t) + diff --git a/app/trading/migrations/0001_initial.py b/app/trading/migrations/0001_initial.py new file mode 100644 index 0000000..54f8032 --- /dev/null +++ b/app/trading/migrations/0001_initial.py @@ -0,0 +1,65 @@ +# Generated by Django 3.2.4 on 2021-07-13 14:27 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='LuxTradeModel', + 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)), + ('entry_price', models.FloatField()), + ('stop_price', models.FloatField()), + ('target_price', models.FloatField()), + ('shares', models.FloatField()), + ('percent_portfolio', models.FloatField()), + ], + ), + migrations.CreateModel( + name='Ticker', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('symbol', models.CharField(max_length=9)), + ('name', models.CharField(blank=True, max_length=243, null=True)), + ], + ), + migrations.CreateModel( + name='TradeJrnl', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateTimeField(auto_now_add=True)), + ('body_markdown', models.TextField(blank=True)), + ('body_html', models.TextField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='OptionsTrade', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateTimeField()), + ('transaction_code', models.CharField(choices=[('Trade', 'Trade')], max_length=25)), + ('transaction_subcode', models.CharField(choices=[('Buy to Open', 'Buy to Open'), ('Sell to Open', 'Sell to Open'), ('Sell to Close', 'Sell to Close')], max_length=25)), + ('buy_sell', models.CharField(choices=[('Buy', 'Buy'), ('Sell', 'Sell')], max_length=4)), + ('open_close', models.CharField(choices=[('Open', 'Open'), ('Close', 'Close')], max_length=5)), + ('quantity', models.FloatField()), + ('expiration_date', models.DateTimeField()), + ('strike', models.FloatField()), + ('call_put', models.CharField(choices=[('C', 'Call'), ('P', 'Put')], max_length=4)), + ('price', models.FloatField()), + ('fees', models.FloatField()), + ('amount', models.FloatField()), + ('description', models.TextField(blank=True)), + ('symbol', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='trading.ticker')), + ], + ), + ] diff --git a/app/trading/migrations/0002_remove_luxtrademodel_percent_portfolio.py b/app/trading/migrations/0002_remove_luxtrademodel_percent_portfolio.py new file mode 100644 index 0000000..b0dc82a --- /dev/null +++ b/app/trading/migrations/0002_remove_luxtrademodel_percent_portfolio.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.4 on 2021-07-13 18:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='luxtrademodel', + name='percent_portfolio', + ), + ] diff --git a/app/trading/migrations/0003_luxtrademodel_status.py b/app/trading/migrations/0003_luxtrademodel_status.py new file mode 100644 index 0000000..bd1b417 --- /dev/null +++ b/app/trading/migrations/0003_luxtrademodel_status.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.4 on 2021-07-13 19:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0002_remove_luxtrademodel_percent_portfolio'), + ] + + operations = [ + migrations.AddField( + model_name='luxtrademodel', + name='status', + field=models.IntegerField(choices=[(0, 'Open'), (1, 'Closed')], default=0), + ), + ] diff --git a/app/trading/migrations/0004_rename_luxtrademodel_luxtrade.py b/app/trading/migrations/0004_rename_luxtrademodel_luxtrade.py new file mode 100644 index 0000000..bcb59d3 --- /dev/null +++ b/app/trading/migrations/0004_rename_luxtrademodel_luxtrade.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.4 on 2021-07-14 07:18 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0003_luxtrademodel_status'), + ] + + operations = [ + migrations.RenameModel( + old_name='LuxTradeModel', + new_name='LuxTrade', + ), + ] diff --git a/app/trading/migrations/0005_auto_20210714_0747.py b/app/trading/migrations/0005_auto_20210714_0747.py new file mode 100644 index 0000000..bd54f71 --- /dev/null +++ b/app/trading/migrations/0005_auto_20210714_0747.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.4 on 2021-07-14 07:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0004_rename_luxtrademodel_luxtrade'), + ] + + operations = [ + migrations.AddField( + model_name='luxtrade', + name='close_date', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='luxtrade', + name='open_date', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AlterField( + model_name='luxtrade', + name='status', + field=models.IntegerField(choices=[(0, 'Open'), (1, 'Closed'), (2, 'Watching')], default=0), + ), + ] diff --git a/app/trading/migrations/0006_luxtrade_close_price.py b/app/trading/migrations/0006_luxtrade_close_price.py new file mode 100644 index 0000000..42f254d --- /dev/null +++ b/app/trading/migrations/0006_luxtrade_close_price.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.4 on 2021-07-14 08:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0005_auto_20210714_0747'), + ] + + operations = [ + migrations.AddField( + model_name='luxtrade', + name='close_price', + field=models.FloatField(blank=True, null=True), + ), + ] diff --git a/app/trading/migrations/0007_alter_luxtrade_status.py b/app/trading/migrations/0007_alter_luxtrade_status.py new file mode 100644 index 0000000..0566b17 --- /dev/null +++ b/app/trading/migrations/0007_alter_luxtrade_status.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.4 on 2021-07-14 09:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('trading', '0006_luxtrade_close_price'), + ] + + operations = [ + migrations.AlterField( + model_name='luxtrade', + name='status', + field=models.IntegerField(choices=[(0, 'Open'), (1, 'Closed'), (2, 'Watching')], default=2), + ), + ] diff --git a/app/trading/migrations/__init__.py b/app/trading/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/trading/migrations/__init__.py diff --git a/app/trading/models.py b/app/trading/models.py new file mode 100644 index 0000000..f9c9881 --- /dev/null +++ b/app/trading/models.py @@ -0,0 +1,174 @@ +import os +from PIL import Image +from django.db import models +from django.db.models.signals import post_save +from django.contrib.sitemaps import Sitemap +from django.dispatch import receiver +from django.urls import reverse +from django.apps import apps +from django.utils.html import format_html +from django.utils import timezone +from django.conf import settings +from django.template.defaultfilters import slugify + +from media.models import LuxImage, LuxImageSize +from media.utils import resize_image +from utils.util import render_images, render_products, parse_video, markdown_to_html + + +def get_upload_path(self, filename): + return "images/products/%s" % (filename) + + +class Ticker(models.Model): + symbol = models.CharField(max_length=9) + name = models.CharField(max_length=243, blank=True, null=True) + + def __str__(self): + return str(self.symbol) + + +class OptionsTrade(models.Model): + date = models.DateTimeField() + symbol = models.ForeignKey(Ticker, null=True, on_delete=models.SET_NULL) + TRANSACTION_CODE = ( + ('Trade', 'Trade'), + ) + transaction_code = models.CharField(choices=TRANSACTION_CODE, max_length=25) + TRANSACTION_SUBCODE = ( + ('Buy to Open', 'Buy to Open'), + ('Sell to Open', 'Sell to Open'), + ('Sell to Close', 'Sell to Close'), + ) + transaction_subcode = models.CharField(choices=TRANSACTION_SUBCODE, max_length=25) + BUY_SELL = ( + ('Buy', 'Buy'), + ('Sell', 'Sell'), + ) + buy_sell = models.CharField(choices=BUY_SELL, max_length=4) + OPEN_CLOSE = ( + ('Open', 'Open'), + ('Close', 'Close'), + ) + open_close = models.CharField(choices=OPEN_CLOSE, max_length=5) + quantity = models.FloatField() + expiration_date = models.DateTimeField() + strike = models.FloatField() + CALL_PUT = ( + ('C', 'Call'), + ('P', 'Put'), + ) + call_put = models.CharField(choices=CALL_PUT, max_length=4) + price = models.FloatField() + fees = models.FloatField() + amount = models.FloatField() + description = models.TextField(blank=True) + + def __str__(self): + return str(self.symbol) + + def get_profit_by_symbol(self,t): + buy_amount = 0 + sell_amount = 0 + for o in OptionsTrade.objects.filter(symbol__symbol=t).filter(buy_sell="Sell"): + sell_amount+=o.amount+o.fees + for o in OptionsTrade.objects.filter(symbol__symbol=t).filter(buy_sell="Buy"): + buy_amount+=o.amount+o.fees + return buy_amount+sell_amount + + def get_profit(self): + buy_amount = 0 + sell_amount = 0 + for o in OptionsTrade.objects.filter(buy_sell="Sell"): + sell_amount+=o.amount+o.fees + for o in OptionsTrade.objects.filter(buy_sell="Buy"): + buy_amount+=o.amount+o.fees + return buy_amount+sell_amount + +class LuxTrade(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() + close_price = models.FloatField(null=True, blank=True) + shares = models.FloatField() + STATUS = ( + (0, 'Open'), + (1, 'Closed'), + (2, 'Watching'), + ) + status = models.IntegerField(choices=STATUS, default=2) + + def __str__(self): + return str(self.symbol) + + def get_absolute_url(self): + return reverse('luxtrade:detail', kwargs={"pk": self.pk}) + + @property + def risk_reward(self): + if self.stop_price > self.entry_price: + return "No risk" + else: + return (self.entry_price - self.stop_price)/(self.target_price-self.entry_price) + + @property + def goal_dollars(self): + return (self.target_price*self.shares)-(self.entry_price*self.shares) + + @property + def goal_percent(self): + return round((((self.target_price*self.shares)-(self.entry_price*self.shares))/self.amount_invested)*100, 2) + + @property + def risk_dollars(self): + if self.stop_price > self.entry_price: + return 0 + else: + return round((self.entry_price-self.stop_price)*self.shares, 2) + + @property + def amount_invested(self): + return round(self.entry_price * self.shares, 2) + + @property + def realized_dollars(self): + return (self.close_price*self.shares)-(self.entry_price*self.shares) + + @property + def realized_percent(self): + return (self.realized_dollars/self.amount_invested)*100 + + def save(self, *args, **kwargs): + if self.status == 0 and not self.open_date: + self.open_date = timezone.now() + super(LuxTrade, self).save() + +class TradeJrnl(models.Model): + date = models.DateTimeField(auto_now_add=True) + body_markdown = models.TextField(blank=True) + body_html = models.TextField(null=True, blank=True) + + def __str__(self): + return str(self.date) + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_read_date() + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[n.id]) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[self.get_next_by_read_date().pk]) + except model.DoesNotExist: + return '' + + def save(self, *args, **kwargs): + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + super(TradeJrnl, self).save() diff --git a/app/trading/templates/products/snippet.html b/app/trading/templates/products/snippet.html new file mode 100644 index 0000000..3fc9f6f --- /dev/null +++ b/app/trading/templates/products/snippet.html @@ -0,0 +1,41 @@ +{% load get_image_by_size %} +{% load get_image_width %} +{% with image=object.featured_image %} +<div itemscope itemtype="http://schema.org/Product" class="product-card"> + <meta itemprop="brand" content="{{object.brand.name}}" /> + <figure itemscope itemtype="http://schema.org/ImageObject" class="picfull"> + <a href="{% get_image_by_size image 'original' %}" title="view larger image"> + <img class="u-photo" itemprop="contentUrl" sizes="(max-width: 750px) 100vw, (min-width: 751) 750px" srcset="{% for size in image.sizes.all%}{% get_image_by_size image size.name %} {{size.width}}w{% if forloop.last%}"{%else%}, {%endif%}{%endfor%}{% for size in image.sizes.all%}{%if not forloop.first and not forloop.last%} src="{% get_image_by_size image size.name%}"{%endif%}{%endfor%} alt="{{image.alt}} photographed by {% if image.photo_credit_source %}{{image.photo_credit_source}}{%else%}luxagraf{%endif%}" > + </a> + <figcaption>{% if image.caption %}{{image.caption|safe}}{% endif %}{% if image.photo_credit_source %}{%if image.caption %} | {%endif%}image: {% if image.photo_credit_url %}<a href="{{image.photo_credit_url}}" itemprop="author">{%endif%}{{image.photo_credit_source|lower}}{% if image.photo_credit_url %}</a>{%endif%}{%endif%} + </figcaption> + </figure> + <div class="buy-btn-wrapper"> + <h4 class="product-header" itemprop="name">{{object.get_full_name}}</h4> + <h5 class="product-link" itemprop="offers" itemscope itemtype="http://schema.org/Offer"> + <a href="{{object.primary_offer_url}}" title="buy the {{object.get_full_name}} for ${{object.primary_offer_price}} from {{object.primary_offer_retailer.get_primary_offer_retailer_display}}" itemprop="url" rel="nofollow"> + Buy Now ({{object.get_primary_offer_retailer_display}} + <span itemprop="priceCurrency" content="USD">$</span><span itemprop="price" content="{{object.primary_offer_price}}">{{object.primary_offer_price}}</span>) + </a> + <link itemprop="availability" href="http://schema.org/InStock" /> + </h5>{% if object.secondary_offer_url %} + <h5 class="product-link" itemprop="offers" itemscope itemtype="http://schema.org/Offer"> + <a href="{{object.secondary_offer_url}}" title="buy the {{object.get_full_name}} for ${{object.secondary_offer_price}} from {{object.secondary_offer_retailer.get_secondary_offer_retailer_display}}" itemprop="url"> + Buy Now ({{object.get_secondary_offer_retailer_display}} + <span itemprop="priceCurrency" content="USD">$</span><span itemprop="price" content="{{object.secondary_offer_price}}">{{object.secondary_offer_price}}</span>) + </a> + <link itemprop="availability" href="http://schema.org/InStock" /> + </h5>{% endif %} + </div> + <span itemprop="review" itemscope itemtype="http://schema.org/Review"> + <meta itemprop="name" content="{{object.get_full_name}}" /> + <meta itemprop="author" content="Scott Gilbertson" /> + <meta itemprop="datePublished" content="{{object.pub_date}}" /> + <span itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating"> + <meta itemprop="worstRating" content = "1"/> + <meta itemprop="ratingValue" content="{{object.rating}}" /> + <meta itemprop="bestRating" content="10" /> + </span> + <meta itemprop="description" content="{{object.body_markdown}}" /> +</div> +{% endwith %} diff --git a/app/trading/templates/trading/base.html b/app/trading/templates/trading/base.html new file mode 100644 index 0000000..86023ba --- /dev/null +++ b/app/trading/templates/trading/base.html @@ -0,0 +1,30 @@ +<html> +<head> + <title>{% block pagetitle %}Luxagraf - Topografical Writings{% endblock %}</title> + <meta charset="utf-8"> + <meta http-equiv="x-ua-compatible" content="ie=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + {%block stylesheet%}<link rel="stylesheet" + href="/media/trading.css{%comment%}?{% now "u" %}{%endcomment%}" + media="screen">{%endblock%} + <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"> + {%block extrahead%}{%endblock%} +</head> + </head> + <body> + <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="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> + <span class="nav-item"><a href="https://client.schwab.com/Areas/Trade/Stocks/Entry.aspx?" target="_blank">Schwab New Trade</a></span> + </nav> + {% block content %} + {% endblock %} + </body> + {% block js %} + {% endblock %} +</html> + diff --git a/app/trading/templates/trading/create_form.html b/app/trading/templates/trading/create_form.html new file mode 100644 index 0000000..521d420 --- /dev/null +++ b/app/trading/templates/trading/create_form.html @@ -0,0 +1,67 @@ +{% extends 'trading/base.html' %} +{% load typogrify_tags %} + {% block content %} + <form id="id_form" action="/trading/model" method="post">{% csrf_token %} + {% for field in form %} + <fieldset> + {{ field.errors }} + {% if field.name == 'status'%} + <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 %} + <input type="submit" name="post" class="btn" value="record purchase"/> + </form> + <dl> + <dt>R/R: </dt><dd id="id_rr"></dd> + <dt>% Portfolio: </dt><dd id="id_p_portfolio"></dd> + <dt>Risk: </dt><dd id="id_risk"></dd> + <dt>Total Invested: </dt><dd id="id_total"></dd> + <dt>$ Goal: </dt><dd id="id_reward"></dd> + <dt>% Goal: </dt><dd id="id_reward_percent"></dd> + </dl> + {% 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 shares = document.getElementById("id_shares").value; + 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 total = entry_price*shares + var percent_goal = (goal/total)*100 + document.getElementById("id_p_portfolio").innerText = (pp*100).toFixed(2); + document.getElementById("id_risk").innerText = "$"+risk_dollars.toFixed(2); + document.getElementById("id_reward").innerText = "$"+goal.toFixed(2); + document.getElementById("id_reward_percent").innerText = percent_goal.toFixed(0)+"%"; + 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 new file mode 100644 index 0000000..fd754b6 --- /dev/null +++ b/app/trading/templates/trading/list.html @@ -0,0 +1,107 @@ +{% extends 'trading/base.html' %} + {% block content %} + <h3>Current Trades</h3> + <table> + <thead> + <tr> + <th>Open Date</th> + <th>Symbol</th> + <th>Entry Price</th> + <th>Stop</th> + <th>Target</th> + <th>Shares</th> + <th>Total Invested</th> + <th>$ at Risk</th> + <th>$ Goal</th> + <th>% Goal</th> + <th>Risk/Reward</th> + </tr> + </thead> + {% for object in open_trades %} + <tr> + <td><a href="{{object.get_absolute_url}}">{{object.date|date:"m-d-Y"}}</a></td> + <td>{{object.symbol}}</td> + <td>${{object.entry_price}}</td> + <td>${{object.stop_price}}</td> + <td>${{object.target_price}}</td> + <td>{{object.shares}}</td> + <td>${{object.amount_invested}}</td> + <td>${{object.risk_dollars}}</td> + <td>${{object.goal_dollars}}</td> + <td>{{object.goal_percent}}</td> + <td>{{object.risk_reward}}</td> + <td><input id="id_close_price"> <span id=profit"></span></td> + </tr> + {% endfor %} + </table> + + <h3>Watching </h3> + <table> + <thead> + <tr> + <th>Open Date</th> + <th>Symbol</th> + <th>Entry Price</th> + <th>Stop</th> + <th>Target</th> + <th>Shares</th> + <th>Total Invested</th> + <th>$ at Risk</th> + <th>$ Goal</th> + <th>% Goal</th> + <th>Risk/Reward</th> + </tr> + </thead> + {% for object in watch_trades %} + <tr> + <td>{{object.date|date:"m-d-Y"}}</td> + <td>{{object.symbol}}</td> + <td>${{object.entry_price}}</td> + <td>${{object.stop_price}}</td> + <td>${{object.target_price}}</td> + <td>{{object.shares}}</td> + <td>${{object.amount_invested}}</td> + <td>${{object.risk_dollars}}</td> + <td>${{object.goal_dollars}}</td> + <td>{{object.goal_percent}}</td> + <td>{{object.risk_reward}}</td> + <td><input id="id_close_price"> <span id=profit"></span></td> + </tr> + {% endfor %} + </table> + <h3>Trade History</h3> + <table> + <thead> + <tr> + <th>Date Purchased</th> + <th>Symbol</th> + <th>Entry Price</th> + <th>Stop Price</th> + <th>Target Price</th> + <th>Number of Shares</th> + <th>Total Invested</th> + <th>$ at Risk</th> + <th>% at Risk</th> + <th>$ Goal</th> + <th>% Goal</th> + <th>Risk/Reward</th> + </tr> + </thead> + {% for object in object_list %} + <tr> + <td>{{object.date}}</td> + <td>{{object.symbol}}</td> + <td>${{object.entry_price}}</td> + <td>${{object.stop_price}}</td> + <td>${{object.target_price}}</td> + <td>{{object.shares}}</td> + <td>${{object.amount_invested}}</td> + <td>${{object.risk_dollars}}</td> + <td>{{object.risk_percent}}</td> + <td>${{object.goal_dollars}}</td> + <td>{{object.goal_percent}}</td> + <td>{{object.risk_reward}}</td> + </tr> + {% endfor %} + </table> + {% endblock %} diff --git a/app/trading/templates/trading/update_form.html b/app/trading/templates/trading/update_form.html new file mode 100644 index 0000000..01e0875 --- /dev/null +++ b/app/trading/templates/trading/update_form.html @@ -0,0 +1,20 @@ +{% extends 'trading/base.html' %} +{% load typogrify_tags %} +{% block content %} + <form id="id_form" action="" method="post">{% csrf_token %} + {% for field in form %} + <fieldset> + {{ field.errors }} + {% if field.name == 'status'%} + <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 %} + <input type="submit" name="post" class="btn" value="update trade"/> + </form> + {% endblock %} diff --git a/app/trading/urls.py b/app/trading/urls.py new file mode 100644 index 0000000..986339a --- /dev/null +++ b/app/trading/urls.py @@ -0,0 +1,24 @@ +from django.urls import path, re_path + +from . import views + +app_name = "luxtrade" + +urlpatterns = [ + path( + 'model', + views.TradeModelFormView.as_view(), + name='testtrade' + ), + path( + 'trade/<pk>', + views.LuxTradeDetailView.as_view(), + name='detail' + ), + path( + '', + views.LuxTradeListView.as_view(), + {'page':1}, + name='list' + ), +] diff --git a/app/trading/views.py b/app/trading/views.py new file mode 100644 index 0000000..a2bc5eb --- /dev/null +++ b/app/trading/views.py @@ -0,0 +1,43 @@ +from django.shortcuts import render +from django.views.generic.edit import FormView +from django.views.generic.edit import CreateView, DeleteView, UpdateView +from utils.views import PaginatedListView + +from .models import OptionsTrade, LuxTrade + + +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'] + 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'] + template_name = 'trading/update_form.html' + success_url = '/trading/' + + +class LuxTradeListView(PaginatedListView): + model = LuxTrade + template_name = 'trading/list.html' + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(LuxTradeListView, self).get_context_data(**kwargs) + context['open_trades'] = LuxTrade.objects.filter(status=0) + context['watch_trades'] = LuxTrade.objects.filter(status=2) + return context + + def get_queryset(self): + queryset = super(LuxTradeListView, self).get_queryset() + return queryset.filter(status=1) |