import os import datetime import calendar from PIL import Image from django.db import models from django.db.models import Sum 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) current_price = models.CharField(max_length=243, blank=True, null=True) price_date = models.DateTimeField(null=True) def __str__(self): return str(self.symbol) class Price(models.Model): symbol = models.ForeignKey(Ticker, null=True, on_delete=models.SET_NULL) price = models.CharField(max_length=243, blank=True, null=True) price_date = models.DateTimeField() 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 LuxTradeStatsManager(models.Manager): def get_month_pl(self, month=timezone.now().month): last_day = calendar.monthrange(timezone.now().year, month)[1] start_date = datetime.date(timezone.now().year, month, 1) end_date = datetime.date(timezone.now().year, month, last_day) return self.filter(close_date__range=(start_date, end_date)).aggregate(Sum('pl')) def get_year_pl(self, year=timezone.now().year): start_date = datetime.date(year, 1, 1) end_date = datetime.date(year, 12, 31) return self.filter(close_date__range=(start_date, end_date)).aggregate(Sum('pl')) 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) notes = models.TextField(null=True, blank=True) notes_html = models.TextField(null=True, blank=True) is_wanderer = models.BooleanField(default=True) pl = models.FloatField(null=True) class Meta: ordering = ('status', '-open_date',) get_latest_by = 'open_date' def __str__(self): return str(self.symbol) objects = models.Manager() # The default manager. stats = LuxTradeStatsManager() 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 round((self.entry_price - self.stop_price)/(self.target_price-self.entry_price),2) @property def goal_dollars(self): return round((self.target_price*self.shares)-(self.entry_price*self.shares), 2) @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 round((self.close_price*self.shares)-(self.entry_price*self.shares), 2) @property def realized_percent(self): return round((self.realized_dollars/self.amount_invested)*100, 2) def save(self, *args, **kwargs): if self.status == 0 and not self.open_date: self.open_date = timezone.now() if self.status == 1 and not self.close_date: self.close_date = timezone.now() if self.status == 1 and not self.pl: self.pl = round((self.close_price*self.shares)-(self.entry_price*self.shares), 2) if self.notes: self.notes_html = markdown_to_html(self.notes) super(LuxTrade, self).save() class LuxOptionsTradeStatsManager(models.Manager): def get_month_pl(self, month=timezone.now().month): last_day = calendar.monthrange(timezone.now().year, month)[1] start_date = datetime.date(timezone.now().year, month, 1) end_date = datetime.date(timezone.now().year, month, last_day) return self.filter(close_date__range=(start_date, end_date)).aggregate(Sum('pl')) def get_year_pl(self, year=timezone.now().year): start_date = datetime.date(year, 1, 1) end_date = datetime.date(year, 12, 31) return self.filter(close_date__range=(start_date, end_date)).aggregate(Sum('pl')) 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() contract_close_price = models.FloatField(null=True, blank=True) 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, blank=True) class Meta: ordering = ('-open_date',) get_latest_by = 'open_date' objects = models.Manager() # The default manager. stats = LuxOptionsTradeStatsManager() 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): if self.call_put == 0: return round(((self.entry_price-self.stop_price)*self.delta/self.contract_price)*self.contract_price*100, 2) else: return round(((self.stop_price-self.entry_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, 2) else: return round((((self.strike_price-self.target_price)*self.number_contracts)*100)-self.fees, 2) @property def days_until_expiration(self): td = self.expiration_date - datetime.date.today() return td.days @property def realized_dollars(self): if self.call_put == 0: return round((((self.contract_close_price*self.number_contracts)*100) - ((self.contract_price*self.number_contracts)*100)-self.fees), 2) else: return round((((self.contract_price*self.number_contracts)*100)-((self.contract_close_price*self.number_contracts)*100)-self.fees), 2) @property def trade_risk_percent(self): return round((self.risk_total/self.amount_invested)*100, 0); @property def portfolio_risk_percent(self): return round((self.risk_total/10000)*100, 0); @property def realized_percent(self): if self.call_put == 0: return round((self.realized_dollars/self.amount_invested)*100, 2) else: return round((self.pl/self.amount_invested)*100, 2) 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: if self.call_put == 0: self.pl = round(((self.contract_close_price*self.number_contracts)*100) - ((self.contract_price*self.number_contracts)*100), 2) else: self.pl = round(((self.contract_close_price*self.number_contracts)*100) - ((self.contract_price*self.number_contracts)*100), 2) if self.notes: self.notes_html = markdown_to_html(self.notes) super(LuxOptionsTrade, 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() class LuxOptionsPurchaseStatsManager(models.Manager): def get_month_pl(self, month=timezone.now().month): last_day = calendar.monthrange(timezone.now().year, month)[1] start_date = datetime.date(timezone.now().year, month, 1) end_date = datetime.date(timezone.now().year, month, last_day) return self.filter(close_date__range=(start_date, end_date)).aggregate(Sum('pl')) def get_year_pl(self, year=timezone.now().year): start_date = datetime.date(year, 1, 1) end_date = datetime.date(year, 12, 31) 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=0) class Meta: ordering = ('-open_date', 'status') get_latest_by = 'open_date' objects = models.Manager() # The default manager. stats = LuxOptionsPurchaseStatsManager() def __str__(self): return str(self.symbol) def get_absolute_url(self): return reverse('luxtrade:option-update', kwargs={"pk": self.pk}) def get_contract_count(self): return self.luxoptioncontract_set.count() @property def profit_loss(self): total = float(0) for option in self.luxoptioncontract_set.all(): if option.contract_close_price: pl = option.contract_close_price - option.contract_open_price else: pl = 0 total = total + pl return round((total*100),2) @property def stop_price(self): return (self.contract_price*.75) @property def total_invested(self): return round(self.get_contract_count()*(self.luxoptioncontract_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.luxoptioncontract_set.first().contract_open_price*1.25 @property def contract_price(self): return self.luxoptioncontract_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: print("updating pl") self.pl = self.profit_loss super(LuxOptionPurchase, self).save() class LuxOptionContract(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())