fisk/core/models.py

223 lines
8.5 KiB
Python
Raw Normal View History

2022-10-13 14:26:43 +00:00
import stripe
2022-10-21 23:15:27 +00:00
from alpaca.trading.client import TradingClient
from alpaca.trading.enums import OrderSide, TimeInForce
from alpaca.trading.requests import LimitOrderRequest, MarketOrderRequest
2022-10-13 17:20:30 +00:00
from django.conf import settings
2022-10-13 14:26:43 +00:00
from django.contrib.auth.models import AbstractUser
from django.db import models
2022-10-17 06:20:30 +00:00
from serde import ValidationError
2022-10-13 17:20:30 +00:00
2022-10-13 14:26:43 +00:00
from core.lib.customers import get_or_create, update_customer_fields
2022-10-17 06:20:30 +00:00
from core.lib.serde import ccxt_s
from core.util import logs
2022-10-13 14:26:43 +00:00
2022-10-17 06:20:30 +00:00
log = logs.get_logger(__name__)
2022-10-13 14:26:43 +00:00
class Plan(models.Model):
name = models.CharField(max_length=255, unique=True)
description = models.CharField(max_length=1024, null=True, blank=True)
cost = models.IntegerField()
product_id = models.CharField(max_length=255, unique=True, null=True, blank=True)
image = models.CharField(max_length=1024, null=True, blank=True)
def __str__(self):
return f"{self.name}{self.cost})"
class User(AbstractUser):
# Stripe customer ID
stripe_id = models.CharField(max_length=255, null=True, blank=True)
last_payment = models.DateTimeField(null=True, blank=True)
plans = models.ManyToManyField(Plan, blank=True)
email = models.EmailField(unique=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._original = self
def save(self, *args, **kwargs):
"""
Override the save function to create a Stripe customer.
"""
if settings.STRIPE_ENABLED:
if not self.stripe_id: # stripe ID not stored
2022-10-13 17:20:30 +00:00
self.stripe_id = get_or_create(
self.email, self.first_name, self.last_name
)
2022-10-13 14:26:43 +00:00
to_update = {}
if self.email != self._original.email:
to_update["email"] = self.email
if self.first_name != self._original.first_name:
to_update["first_name"] = self.first_name
if self.last_name != self._original.last_name:
to_update["last_name"] = self.last_name
update_customer_fields(self.stripe_id, **to_update)
super().save(*args, **kwargs)
def delete(self, *args, **kwargs):
if settings.STRIPE_ENABLED:
if self.stripe_id:
stripe.Customer.delete(self.stripe_id)
2022-10-17 06:20:30 +00:00
log.info(f"Deleted Stripe customer {self.stripe_id}")
2022-10-13 14:26:43 +00:00
super().delete(*args, **kwargs)
def has_plan(self, plan):
plan_list = [plan.name for plan in self.plans.all()]
return plan in plan_list
2022-10-17 17:56:16 +00:00
class Account(models.Model):
2022-10-21 23:15:27 +00:00
EXCHANGE_CHOICES = (("alpaca", "Alpaca"),)
2022-10-17 17:56:16 +00:00
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
2022-10-18 06:22:22 +00:00
exchange = models.CharField(choices=EXCHANGE_CHOICES, max_length=255)
2022-10-17 17:56:16 +00:00
api_key = models.CharField(max_length=255)
api_secret = models.CharField(max_length=255)
2022-10-18 06:22:22 +00:00
sandbox = models.BooleanField(default=False)
2022-10-17 17:56:16 +00:00
def get_account(self):
2022-10-21 23:15:27 +00:00
trading_client = TradingClient(
self.api_key, self.api_secret, paper=self.sandbox
)
return trading_client.get_account()
2022-10-17 17:56:16 +00:00
2022-10-13 14:26:43 +00:00
class Session(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
request = models.CharField(max_length=255, null=True, blank=True)
session = models.CharField(max_length=255, null=True, blank=True)
subscription_id = models.CharField(max_length=255, null=True, blank=True)
plan = models.ForeignKey(Plan, null=True, blank=True, on_delete=models.CASCADE)
2022-10-14 06:20:30 +00:00
class Hook(models.Model):
2022-10-12 06:22:22 +00:00
user = models.ForeignKey(User, on_delete=models.CASCADE)
2022-10-15 22:00:02 +00:00
name = models.CharField(max_length=1024, null=True, blank=True, unique=True)
hook = models.CharField(max_length=255, unique=True)
2022-10-14 06:20:30 +00:00
received = models.IntegerField(default=0)
2022-10-17 17:56:16 +00:00
class Trade(models.Model):
2022-10-17 06:20:30 +00:00
SYMBOL_CHOICES = (
("BTC/USD", "Bitcoin/US Dollar"),
("LTC/USD", "Litecoin/US Dollar"),
)
2022-10-18 06:22:22 +00:00
TYPE_CHOICES = (
("market", "Market"),
("limit", "Limit"),
)
DIRECTION_CHOICES = (
("buy", "Buy"),
("sell", "Sell"),
)
2022-10-17 17:56:16 +00:00
account = models.ForeignKey(Account, on_delete=models.CASCADE)
hook = models.ForeignKey(Hook, on_delete=models.CASCADE, null=True, blank=True)
2022-10-18 06:22:22 +00:00
symbol = models.CharField(choices=SYMBOL_CHOICES, max_length=255)
type = models.CharField(choices=TYPE_CHOICES, max_length=255)
2022-10-17 17:56:16 +00:00
amount = models.FloatField()
2022-10-17 06:20:30 +00:00
price = models.FloatField(null=True, blank=True)
2022-10-17 17:56:16 +00:00
stop_loss = models.FloatField(null=True, blank=True)
take_profit = models.FloatField(null=True, blank=True)
2022-10-18 06:22:22 +00:00
status = models.CharField(max_length=255, null=True, blank=True)
2022-10-21 23:15:27 +00:00
direction = models.CharField(choices=DIRECTION_CHOICES, max_length=255)
2022-10-18 06:22:22 +00:00
2022-10-17 06:20:30 +00:00
# To populate from the trade
order_id = models.CharField(max_length=255, null=True, blank=True)
client_order_id = models.CharField(max_length=255, null=True, blank=True)
response = models.JSONField(null=True, blank=True)
2022-10-18 06:22:22 +00:00
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._original = self
def save(self, *args, **kwargs):
"""
Override the save function to place the trade.
"""
2022-10-17 06:20:30 +00:00
if self.response is None:
2022-10-18 06:22:22 +00:00
# the trade is not placed yet
if self.account.exchange == "alpaca":
2022-10-21 23:15:27 +00:00
trading_client = TradingClient(
self.account.api_key,
self.account.api_secret,
paper=self.account.sandbox,
2022-10-21 23:15:27 +00:00
)
if self.direction == "buy":
direction = OrderSide.BUY
elif self.direction == "sell":
direction = OrderSide.SELL
else:
raise Exception("Unknown direction")
2022-10-18 06:22:22 +00:00
if self.type == "market":
market_order_data = MarketOrderRequest(
symbol=self.symbol,
qty=self.amount,
side=OrderSide.BUY,
time_in_force=TimeInForce.IOC,
2022-10-18 06:22:22 +00:00
)
order = trading_client.submit_order(order_data=market_order_data)
2022-10-18 06:22:22 +00:00
elif self.type == "limit":
limit_order_data = LimitOrderRequest(
symbol=self.symbol,
limit_price=self.price,
qty=self.amount,
side=direction,
time_in_force=TimeInForce.IOC,
2022-10-18 06:22:22 +00:00
)
order = trading_client.submit_order(order_data=limit_order_data)
else:
raise Exception("Unknown order type")
2022-10-17 06:20:30 +00:00
print("ORDER", order)
# self.status = parsed.status
# self.response = order
2022-10-18 06:22:22 +00:00
else:
# there is a trade open
# get trade
# update trade
pass
super().save(*args, **kwargs)
def delete(self, *args, **kwargs):
# close the trade
super().delete(*args, **kwargs)
2022-10-17 17:56:16 +00:00
class Callback(models.Model):
hook = models.ForeignKey(Hook, on_delete=models.CASCADE)
title = models.CharField(max_length=1024, null=True, blank=True)
message = models.CharField(max_length=1024, null=True, blank=True)
period = models.CharField(max_length=255, null=True, blank=True)
2022-10-16 13:37:49 +00:00
timestamp_sent = models.BigIntegerField(null=True, blank=True)
timestamp_trade = models.BigIntegerField(null=True, blank=True)
market_exchange = models.CharField(max_length=255, null=True, blank=True)
market_item = models.CharField(max_length=255, null=True, blank=True)
market_currency = models.CharField(max_length=255, null=True, blank=True)
market_contract = models.CharField(max_length=255, null=True, blank=True)
2022-10-13 14:26:43 +00:00
# class Perms(models.Model):
# class Meta:
# permissions = (
# ("bypass_hashing", "Can bypass field hashing"), #
# ("bypass_blacklist", "Can bypass the blacklist"), #
# ("bypass_encryption", "Can bypass field encryption"), #
# ("bypass_obfuscation", "Can bypass field obfuscation"), #
# ("bypass_delay", "Can bypass data delay"), #
# ("bypass_randomisation", "Can bypass data randomisation"), #
# ("post_irc", "Can post to IRC"),
# ("post_discord", "Can post to Discord"),
# ("query_search", "Can search with query strings"), #
# ("use_insights", "Can use the Insights page"),
# ("index_int", "Can use the internal index"),
# ("index_meta", "Can use the meta index"),
# ("restricted_sources", "Can access restricted sources"),
# )