from chembl_webresource_client.new_client import new_client
from utils import printerr
from tqdm import tqdm
import sys

if (len(sys.argv) < 2):
    exit("You must provide the minimum inhibition percentage at 1 micromol as argument.")
min_inhibition_percentage = int(sys.argv[1])

assay = new_client.assay

# Filtramos los assay para quedarnos solo con los GSK
assay_results = assay.filter(
    description__contains='GSK_PKIS',
    assay_type='B',
    relationship_type='D').only(['document_chembl_id'])

printerr('Number of Assay Search Results: ' + str(len(assay_results)))

# Vemos cuantos fármacos diferentes tenemos
doc_list = list()
for e in assay_results:
    doc_list.append(e['document_chembl_id'])

doc_list = list(set(doc_list))

printerr('Documents found in search: ' + str(doc_list))

printerr('Filtering activities with doc id in previous query...')

activity = new_client.activity

# Hacemos otra búsqueda para obtener las interacciones
interactions = activity.filter(
    document_chembl_id__in=doc_list,
    assay_type='B',
    relationship_type='D',
    assay_description__contains='at 1 uM', # pchembl = -log(IC50) = -log(10^-6)=6
    value__gte=min_inhibition_percentage).only(['target_chembl_id', 'molecule_chembl_id'])

printerr('Number of Bioactivity Search Results: ' + str(len(interactions)))

for interaction in tqdm(interactions):
    print(u"%s\t%s" % (interaction['target_chembl_id'], interaction['molecule_chembl_id']))


class PDBWriter():
    """
    THINK IVE FUCKED SOMETHING UP IN HERE
    """
    def string_builder(self, i, atom_name, res_name, res_id, res_number, coord):
        """
        Return line of pdb file
        """
        line = 'ATOM' + ' '*2                                 # “ATOM” 1-4
        line += ' '*(5-len(str(i+1))) + str(i+1)              # Atom Serial Number 7-11
        line += ' '*2 + atom_name + ' '*(3-len(atom_name))    # Atom Name 13-16
        line += ' '                                           # Alternate Location Indicator 17
        line += res_name                                      # Residue name 18-20
        line += ' ' + res_id                                  # Chain ID 22
        line += ' '*(4-len(str(res_number))) + str(res_number)# Residue Sequence number 23-26
        line += ' '*4                                         # Code for insertions of residues 27
        line += ' '*(8-len(str(coord[0]))) + str(coord[0])    # X coord 31-38
        line += ' '*(8-len(str(coord[1]))) + str(coord[1])    # Y coord 39-46
        line += ' '*(8-len(str(coord[2]))) + str(coord[2])    # Z coord 47-54
        line += ' '*2 + '1.00'                                # Occupancy 55-60
        line += ' ' + '30.00'                                 # Temperature factor 61-66
        line += ' '*5 + ' '*5                                 # Segment identifier 73-76
        line += ' ' + atom_name[0]                            # Element Symbol 77-78
        line += '\n'

        return line

    def write_to_pdb(self, filename, residue_names, residue_ids, residue_coords):
        '''
        Write out calculated coords to PDB
        '''

        atom_names = ['N', 'CA', 'C']*(len(residue_names))
        with open(filename, 'w') as f:
            atom_coords = []
            for atom_coord in residue_coords:
                atom_coords.append([np.around(c, 3) for c in atom_coord])

            res_number = 0
            for i, coord in enumerate(atom_coords):

                if i % 3 == 0:
                    res_number += 1

                if residue_names[res_number-1] == "XXX" or residue_ids[res_number-1] == "X":
                    break
                f.write(self.string_builder(i, atom_names[i], residue_names[res_number-1],
                                            residue_ids[res_number-1], res_number, coord))
            f.write("END")


from .base import BaseOrder, BaseUserOrder
from datetime import timedelta
from decimal import Decimal

from django.conf import settings
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext as _

from core.models import Transaction, Currency
from payments.utils import money_format
from payments.models import Payment, PaymentPreference
from ticker.models import Price
from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.db.models import Q
from math import log10, floor, ceil
from django.utils.translation import activate
from nexchange.utils import send_email, send_sms, get_nexchange_logger
from django_fsm import FSMIntegerField, transition
from nexchange.celery import app
from cached_property import cached_property_with_ttl
from payments.api_clients.safe_charge import SafeChargeAPIClient
from nexchange.api_clients.factory import ApiClientFactory
from oauth2_provider.models import AccessToken
from decimal import InvalidOperation
from verification.api_clients.idenfy import IdenfyAPIClient
from verification.models import IdentityToken
from payments.utils import get_first_name, get_last_name
from .fee import OrderFee, FeeSource

safe_charge_client = SafeChargeAPIClient()
idenfy_client = IdenfyAPIClient()
factory = ApiClientFactory()
logger = get_nexchange_logger('Order logger', with_email=True,
                              with_console=True)

class Order(BaseOrder, BaseUserOrder):

    def __init__(self, *args, **kwargs):
        self.fee_list = {'update': False}
        super(Order, self).__init__(*args, **kwargs)

    # these are redifined to pass to django FSM transitions
    INITIAL = BaseUserOrder.INITIAL
    PAID_UNCONFIRMED = BaseUserOrder.PAID_UNCONFIRMED
    PAID = BaseUserOrder.PAID
    PRE_RELEASE = BaseUserOrder.PRE_RELEASE
    RELEASED = BaseUserOrder.RELEASED
    COMPLETED = BaseUserOrder.COMPLETED
    CANCELED = BaseUserOrder.CANCELED
    REFUNDED = BaseUserOrder.REFUNDED
    STATUS_TYPES = BaseUserOrder.STATUS_TYPES
    _order_status_help = BaseUserOrder._order_status_help

    RETRY_RELEASE = 'orders.task_summary.release_retry_invoke'

    PROVIDED_BASE = 0
    PROVIDED_QUOTE = 1
    PROVIDED_BOTH = 2
    PROVIDED_AMOUNT_OPTIONS = (
        (PROVIDED_BASE, _('amount_base')),
        (PROVIDED_QUOTE, _('amount_quote')),
        (PROVIDED_BOTH, _('amount_quote and amount_base')),
    )

    # Todo: inherit from BTC base?, move lengths to settings?
    status = FSMIntegerField(choices=STATUS_TYPES, default=INITIAL,
                             help_text=_order_status_help)
    payment_window = models.IntegerField(default=settings.PAYMENT_WINDOW)
    unpaid_order_window = models.IntegerField(
        default=settings.UNPAID_CANCEL_WINDOW_MINUTES
    )
    payment_preference = models.ForeignKey('payments.PaymentPreference',
                                           default=None,
                                           null=True, blank=True,
                                           on_delete=models.DO_NOTHING)
    is_default_rule = models.BooleanField(default=False)
    from_default_rule = models.BooleanField(default=False)
    price = models.ForeignKey(Price, null=True, blank=True,
                              on_delete=models.DO_NOTHING)
    user_marked_as_paid = models.BooleanField(default=False)
    system_marked_as_paid = models.BooleanField(default=False)
    user_provided_amount = models.IntegerField(
        choices=PROVIDED_AMOUNT_OPTIONS, default=PROVIDED_BASE)
    slippage = models.DecimalField(
        max_digits=18, decimal_places=8, default=Decimal('0'),
    )
    set_as_paid_unconfirmed_on = models.DateTimeField(null=True, blank=True,
                                                      default=None)
    set_as_paid_on = models.DateTimeField(null=True, blank=True, default=None)
    set_as_released_on = models.DateTimeField(null=True, blank=True,
                                              default=None)
    return_identity_token = models.BooleanField(
        default=True,
        help_text=_('If True - user can use auto id checking service(Idenfy).')
    )
    referred_with = models.ForeignKey(
        'referrals.ReferralCode', default=None, null=True, blank=True,
        on_delete=models.DO_NOTHING
    )

    class Meta:
        ordering = ['-created_on']
        # unique_together = ['deleted', 'unique_reference']

    def validate_unique(self, exclude=None):
        # TODO: exclude expired?
        if not self.deleted and \
                Order.objects.exclude(pk=self.pk).filter(
                    unique_reference=self.unique_reference,
                    deleted=False).exists():
            raise ValidationError(
                'Un-deleted order with same reference exists')

        super(Order, self).validate_unique(exclude=exclude)

    def _types_range_constraint(self, field, types):
        """ This is used for validating IntegerField's with choices.
        Assures that value is in range of choices.
        """
        if field > max([i[0] for i in types]):
            raise ValidationError(_('Invalid order type choice'))
        elif field < min([i[0] for i in types]):
            raise ValidationError(_('Invalid order type choice'))

    def get_amount_base_min(self, user_format=False):
        _base_min = self.pair.base.minimal_amount
        if not user_format:
            return _base_min
        _quote_min = self.get_amount_quote_min(user_format=False)
        if _quote_min is None:
            return _base_min
        _min_list = [_base_min]
        _order = Order(pair=self.pair, amount_quote=_quote_min,
                       price=self.price)
        _order.calculate_base_from_quote()
        diff = settings.SATOSHI if self.pair.quote.is_crypto else Decimal('0')
        _min_list.append(_order.amount_base + diff)
        return max(_min_list)

    def get_amount_base_max(self, user_format=False):
        if self.pk:
            return
        _base_max = None if self.pair.base.execute_cover \
            else self.pair.base.available_main_reserves
        if not user_format:
            return _base_max
        _quote_max = self.get_amount_quote_max(user_format=False)
        if _quote_max is None:
            return _base_max
        _max_list = [_base_max] if _base_max else []
        _order = Order(pair=self.pair, amount_quote=_quote_max,
                       price=self.price)
        _order.calculate_base_from_quote()
        diff = settings.SATOSHI if self.pair.base.is_crypto else Decimal('0')
        _max_list.append(_order.amount_base - diff)
        return min(_max_list)

    def get_amount_quote_max(self, user_format=False):
        if self.pk:
            return
        _quote_max = self.pair.quote.maximal_amount
        if not user_format:
            return _quote_max
        _base_max = self.get_amount_base_max(user_format=False)
        if not _base_max:
            return _quote_max
        _max_list = [_quote_max]
        _order = Order(pair=self.pair, amount_base=_base_max,
                       price=self.price)
        _order.calculate_quote_from_base()
        diff = settings.SATOSHI if self.pair.quote.is_crypto else Decimal('0')
        _max_list.append(_order.amount_quote - diff)
        return min(_max_list)

    def get_amount_quote_min(self, user_format=False):
        if self.pk:
            return
        _quote_min = \
            None if self.pair.is_crypto else self.pair.quote.minimal_amount
        if not user_format:
            return _quote_min
        _base_min = self.get_amount_base_min(user_format=False)
        if not _base_min:
            return _quote_min
        _min_list = [_quote_min] if _quote_min else []
        _order = Order(pair=self.pair, amount_base=_base_min,
                       price=self.price)
        _order.calculate_quote_from_base()
        diff = settings.SATOSHI if self.pair.quote.is_crypto else Decimal('0')
        _min_list.append(_order.amount_quote + diff)
        return max(_min_list)

    def _validate_order_amount(self):
        _raise_error = False
        # _error_type is either 'min' or 'max'
        _error_type = None
        _error_amount_type = \
            'amount_base' if self.user_provided_amount == self.PROVIDED_BASE \
            else 'amount_quote'
        # _direction is either 'receive' or 'deposit' (terms form UI)
        _direction = \
            'receive' if self.user_provided_amount == self.PROVIDED_BASE \
            else 'deposit'
        _currency_code = \
            self.pair.base.code \
            if self.user_provided_amount == self.PROVIDED_BASE \
            else self.pair.quote.code
        # actual restricted amount (might be converted)
        _final_amount = None
        # Message template - end user must understand this
        _error_template = \
            '{_error_type}imum {_direction} amount is {_final_amount} ' \
            '{_currency_code} on this trade.'

        for _amount_type in ['amount_quote', 'amount_base']:
            _min = getattr(self, 'get_{}_min'.format(_amount_type))()
            _max = getattr(self, 'get_{}_max'.format(_amount_type))()
            _amount = getattr(self, _amount_type)
            if _min is not None and _amount < _min:
                _raise_error = True
                _error_type = 'min'
                break
            if _max is not None and _amount > _max:
                _raise_error = True
                _error_type = 'max'
                break

        if _raise_error:
            _final_amount = getattr(
                self, 'get_{}_{}'.format(_error_amount_type, _error_type)
            )(user_format=True)
            if _final_amount is None:
                _error_msg = \
                    'Cannot initiate this trade, please contact support.'
            else:
                _error_msg = _error_template.format(
                    _error_type=_error_type.capitalize(),
                    _direction=_direction,
                    _currency_code=_currency_code,
                    _final_amount=str(_final_amount).rstrip('0').rstrip('.'),
                )
            raise ValidationError(_error_msg)

    def _validate_status(self, status):
        if not self.pk:
            return
        old_order = Order.objects.get(pk=self.pk)
        old_status = old_order.status
        if old_status == status:
            return
        elif old_status == Order.CANCELED and status != Order.CANCELED:
            raise ValidationError(
                _('Cannot set order status to {} when it is CANCELED'.format(
                    status)))
        elif status == Order.CANCELED:
            if old_status in [Order.INITIAL, Order.CANCELED,
                              Order.PAID_UNCONFIRMED]:
                return
            else:
                raise ValidationError(
                    _('Cannot CANCEL order which is status is {}'.format(
                        old_order.get_status_display()
                    ))
                )
        elif status == Order.REFUNDED:
            if old_status in self.REFUNDABLE_STATES:
                return
            else:
                raise ValidationError(
                    _('Cannot REFUND order which is status is {}'.format(
                        old_order.get_status_display()
                    ))
                )
        elif status < old_status:
            raise ValidationError(
                _('Cannot set order status from {} to {}'
                  '(must be incremential)'.format(old_status, status)))
        else:
            return

    def _validate_pair(self):
        if not self.pk:
            self.pair.refresh_from_db()
            self.pair.validate_enabled()
            self.pair.validate_user(self.user)

    def _validate_price(self):
        if not self.pk and self.price.expired:
            error_msg = 'Pair {} is temporarily unavailable.'.format(
                self.pair.name
            )
            logger.warning('{}. Ticker expired !!!'.format(error_msg))
            raise ValidationError(
                _(error_msg.format(self.pair.name))
            )

    def _validate_reserves(self):
        error_msg = 'Pair {} is temporarily unavailable.'.format(
            self.pair.name
        )
        if not self.pk:
            if self.pair.base.is_crypto:
                base_reserves = self.pair.base.available_reserves
                min_base_reserves = self.pair.base.reserve.minimum_level
                if base_reserves < min_base_reserves:
                    logger.warning(
                        '{}. Base Reserves too low.'.format(error_msg)
                    )
                    raise ValidationError(_(error_msg))
            if self.pair.quote.is_crypto:
                quote_reserves = self.pair.quote.available_reserves
                min_quote_reserves = self.pair.quote.reserve.maximum_level
                if quote_reserves > min_quote_reserves:
                    logger.warning('{}. Quote Reserves too high.'.format(
                        error_msg
                    ))
                    logger.warning(error_msg)
                    raise ValidationError(_(error_msg))

    def _validate_address(self):
        if not self.pk and getattr(self.withdraw_address, 'blocked', False):
            raise ValidationError(_('Address {} is blocked.'.format(
                self.withdraw_address.address
            )))

    def _validate_fields(self):
        self._types_range_constraint(self.order_type, self.TYPES)
        self._types_range_constraint(self.status, self.STATUS_TYPES)
        self._validate_status(self.status)
        self._validate_pair()
        self._validate_address()
        if all([not self.amount_base, not self.amount_quote]):
            raise ValidationError(
                _('One of amount_quote and amount_base is required.'))
        if all([not self.pair.includes_currency('XMR'),
                self.payment_id is not None]):
            raise ValidationError(
                _('Payment id is currently used only for Monero address'))
        if all([not self.pair.includes_currency('XRP'),
                self.destination_tag is not None]):
            raise ValidationError(
                _('Destination tag is currently used only for Ripple address'))

    def clean(self, *args, **kwargs):
        self._validate_fields()
        super(Order, self).clean(*args, **kwargs)

    def set_provided_amount_option(self):
        if not self.pk:
            self.user_provided_amount = self.get_provided_amount_option()

    def get_provided_amount_option(self):
        if all([self.amount_quote, self.amount_base]):
            return self.PROVIDED_BOTH
        elif self.amount_base:
            return self.PROVIDED_BASE
        elif self.amount_quote:
            return self.PROVIDED_QUOTE

    def set_payment_preference(self, method_name='Safe Charge'):
        if any([self.pair.is_crypto, self.pk, self.payment_preference]):
            return
        self.payment_preference = PaymentPreference.objects.get(
            user__is_staff=True,
            payment_method__name__icontains=method_name
        )

    def save(self, *args, **kwargs):
        self._validate_fields()
        if not self.unique_reference:
            self.unique_reference = \
                self.gen_unique_value(
                    lambda x: self.get_random_unique_reference(x),
                    lambda x: Order.objects.filter(unique_reference=x).count(),
                    settings.UNIQUE_REFERENCE_LENGTH
                )
        if all([self.pair.quote.code == 'XMR', not self.payment_id]):
            self.payment_id = self.gen_unique_payment_id(32)
        if all([self.pair.quote.code == 'XRP', not self.destination_tag]):
            self.destination_tag = \
                self.gen_destination_tag(
                    lambda: self.get_random_integer(),
                    lambda x: Order.objects.filter(destination_tag=x).count()
                )
        if self.pair.is_crypto:
            self.exchange = True
        else:
            self.exchange = False
        if not self.pk:
            if self.amount_base:
                self.calculate_quote_from_base()
            elif self.amount_quote:
                self.calculate_base_from_quote()
        self._validate_order_amount()
        self._validate_price()
        self._validate_reserves()
        self.referred_with = self._referred_with
        super(Order, self).save(*args, **kwargs)
        if self.fee_list.get('update', False):
            tot_fee_amount_base = tot_fee_amount_quote = Decimal('0')
            for _fee in self.fee_list.get('data', []):
                fee_source = _fee.get('fee_source')
                amount_quote = _fee.get('amount_quote')
                amount_base = _fee.get('amount_base')
                fee, _ = OrderFee.objects.get_or_create(
                    fee_source=fee_source, order=self
                )
                fee.amount_base = amount_base
                tot_fee_amount_base += \
                    amount_base if amount_base else Decimal('0')
                fee.amount_quote = amount_quote
                tot_fee_amount_quote += \
                    amount_quote if amount_quote else Decimal('0')
                fee.save()
            markup_multiplier = \
                Decimal('1') / (Decimal('1') + self.pair.fee_ask_current) \
                * self.pair.fee_ask_current
            markup_base = \
                (self.amount_base - tot_fee_amount_base) * markup_multiplier
            markup_quote = \
                (self.amount_quote - tot_fee_amount_quote) * markup_multiplier
            markup_fee, _ = OrderFee.objects.get_or_create(
                fee_source=self.get_or_create_fee_source(OrderFee.MARKUP),
                order=self
            )
            markup_fee.amount_base = markup_base
            markup_fee.amount_quote = markup_quote
            markup_fee.save()
            self.fee_list.update({'update': False})
        if not self.exchange and self.payment_set.count():
            payment = self.payment_set.last()
            if payment and payment.payment_preference:
                pref = payment.payment_preference
                pref.save(update_fields=['has_pending_orders'])

    def calculate_quote_from_base(self, price=None):
        try:
            self.calculate_from(_from='base', price=price)
        except InvalidOperation:
            raise ValidationError(
                _('It is not possible to receive {} {}'.format(
                    self.amount_base,
                    self.pair.base.code
                ))
            )

    def calculate_base_from_quote(self, price=None):
        try:
            self.calculate_from(_from='quote', price=price)
        except InvalidOperation:
            raise ValidationError(
                _('It is not possible to deposit {} {}'.format(
                    self.amount_quote,
                    self.pair.quote.code
                ))
            )

    def set_slippage(self, price, amount, amount_type):
        currency = self.pair.base
        quote = self.pair.quote
        if amount_type == 'base':
            additional_amount = amount
        elif amount_type == 'quote':
            additional_amount = amount / price.rate

        slippage = self.get_current_slippage(
            currency, quote, additional_amount=additional_amount
        )
        self.slippage = slippage
        currency.current_slippage = slippage
        currency.save()
        price.slippage = slippage
        price.save()

    def convert_from(self, amount, _from):
        if amount is None or not self.price:
            return
        rate = self.price.ticker.ask if self.order_type == self.BUY else self.\
            price.ticker.bid
        if _from == 'base':
            return amount * rate
        elif _from == 'quote':
            return amount / rate

    def get_or_create_fee_source(self, name):
        sources = FeeSource.objects.filter(name=name)
        return sources.latest('id') if sources else FeeSource.objects.create(
            name=name
        )

    def calculate_from(self, _from='base', price=None):
        self.set_provided_amount_option()
        self.set_payment_preference()
        if _from == 'base':
            _to = 'quote'
        elif _from == 'quote':
            _to = 'base'
        else:
            raise NotImplementedError('Not implemented conversion')
        amount_input = Decimal(getattr(self, 'amount_{}'.format(_from)))
        setattr(self, 'amount_{}'.format(_from), amount_input)
        if not price:
            price = self.pair.latest_price
        self.price = price
        self.set_slippage(self.price, amount_input, _from)
        ticker_amount_output = getattr(self, 'ticker_amount_{}'.format(_to))
        fee_adder = getattr(self, 'add_payment_fee_to_amount_{}'.format(_to))
        amount_output, withdrawal_fee, payment_fee = fee_adder(
            ticker_amount_output
        )
        self.fee_list.update({
            'update': True,
            'data': [
                {'amount_{}'.format(_to): withdrawal_fee,
                 'amount_{}'.format(_from): self.convert_from(withdrawal_fee,
                                                              _to),
                 'fee_source': self.get_or_create_fee_source(
                     OrderFee.WITHDRAWAL),
                 'order': self},
                {'amount_{}'.format(_to): payment_fee,
                 'amount_{}'.format(_from): self.convert_from(payment_fee,
                                                              _to),
                 'fee_source': self.get_or_create_fee_source(
                     OrderFee.PAYMENT_METHOD),
                 'order': self},
            ]})
        decimal_places_output = getattr(
            self, 'recommended_{}_decimal_places'.format(_to))
        amount_output_formatted = money_format(amount_output,
                                               places=decimal_places_output)
        setattr(self, 'amount_{}'.format(_to), amount_output_formatted)
        decimal_places_input = getattr(
            self, 'recommended_{}_decimal_places'.format(_from))
        amount_input_formatted = money_format(amount_input,
                                              places=decimal_places_input)
        setattr(self, 'amount_{}'.format(_from), amount_input_formatted)

    @property
    def payment_url(self):
        if any([self.pair.quote.is_crypto, self.status != self.INITIAL]):
            return
        return safe_charge_client.generate_cachier_url_for_order(self)

    def get_fullname(self):
        if self.payment_set.count() == 1:
            pref = self.payment_set.get().payment_preference
            fullname = pref.secondary_identifier
            return fullname if fullname else ''
        return ''

    def get_or_create_identity_token(self):
        try:
            token = IdentityToken.objects.filter(order=self).latest('id')
        except ObjectDoesNotExist:
            token = None
        if not token or token.expired or token.used:
            fullname = self.get_fullname()
            first_name = get_first_name(fullname)
            last_name = get_last_name(fullname)
            _token, _scan_ref = idenfy_client.get_token_for_order(
                self,
            )
            if not _token:
                return
            token = IdentityToken.objects.create(order=self, token=_token,
                                                 first_name=first_name,
                                                 last_name=last_name,
                                                 scan_ref=_scan_ref)
        return token

    @property
    def identity_token(self):
        if any([self.pair.quote.is_crypto,
                self.status != self.PAID_UNCONFIRMED,
                not self.return_identity_token]):
            return
        token = self.get_or_create_identity_token()
        return token.token if token else None

    @property
    def identity_check_url(self):
        token = self.identity_token
        return idenfy_client.get_redirect_url(token)

    @cached_property_with_ttl(ttl=settings.TICKER_INTERVAL)
    def amount_eur(self):
        return Price.convert_amount(self.amount_quote,
                                    self.pair.quote, 'EUR')

    @cached_property_with_ttl(ttl=settings.TICKER_INTERVAL)
    def amount_usd(self):
        return Price.convert_amount(self.amount_quote,
                                    self.pair.quote, 'USD')

    @cached_property_with_ttl(ttl=settings.TICKER_INTERVAL)
    def amount_btc(self):
        return Price.convert_amount(self.amount_quote,
                                    self.pair.quote, 'BTC')

    @property
    def ticker_amount_quote(self):
        if not self.price:
            return self.amount_quote
        # For annotations
        res = None
        if self.order_type == Order.BUY:
            res = self.amount_base * self.price.rate
        elif self.order_type == Order.SELL:
            res = self.amount_base * self.price.ticker.bid
        return res

    @property
    def ticker_amount_base(self):
        if not self.price:
            return self.amount_base
        # For annotations
        res = None
        if self.order_type == Order.BUY:
            res = self.amount_quote / self.price.rate
        elif self.order_type == Order.SELL:
            res = self.amount_quote / self.price.ticker.bid
        return res

    @property
    def dynamic_decimal_places(self):

        return False

    @property
    def recommended_quote_decimal_places(self):
        return self.recommended_decimal_places(self.ticker_amount_quote,
                                               self.pair.quote,
                                               dynamic=self.dynamic_decimal_places)  # noqa

    @property
    def recommended_base_decimal_places(self):
        return self.recommended_decimal_places(self.ticker_amount_base,
                                               self.pair.base,
                                               dynamic=self.dynamic_decimal_places)  # noqa

    @property
    def withdrawal_fee(self):
        return self.pair.base.withdrawal_fee

    @property
    def withdrawal_fee_quote(self):
        if self.order_type == self.BUY:
            return self.pair.base.withdrawal_fee * self.price.ticker.ask
        if self.order_type == self.SELL:
            return self.pair.base.withdrawal_fee * self.price.ticker.bid

    def recommended_decimal_places(self, amount, currency, dynamic=False):
        decimal_places = currency.rounding
        if dynamic and currency.is_crypto:
            add_places = -int(floor(log10(abs(amount))))
            _add_places = add_places if add_places > 0 else 0
            decimal_places = 2 + _add_places
        return decimal_places

    def _get_minimal_payment_method_fee(self, currency):
        payment_method = self.payment_preference.payment_method
        min_fee = payment_method.minimal_fee_amount
        min_curr = payment_method.minimal_fee_currency
        return Price.convert_amount(min_fee, min_curr, currency)

    @property
    def minimal_payment_method_fee_quote(self):
        return self._get_minimal_payment_method_fee(self.pair.quote)

    @property
    def minimal_payment_method_fee_base(self):
        return self.minimal_payment_method_fee_quote / self.price.rate

    def add_payment_fee_to_amount_base(self, amount_base):
        payment_fee = None
        if any([not self.payment_preference, self.pair.is_crypto]):
            res = amount_base
        else:
            method = self.payment_preference.payment_method
            fee = method.fee_deposit
            fee_amount = fee * amount_base
            if fee_amount >= self.minimal_payment_method_fee_base:
                payment_fee = fee_amount
                res = amount_base - fee_amount
            else:
                payment_fee = self.minimal_payment_method_fee_base
                res = amount_base - self.minimal_payment_method_fee_base
        withdrawal_fee = self.withdrawal_fee
        return res - withdrawal_fee, withdrawal_fee, payment_fee

    def add_payment_fee_to_amount_quote(self, amount_quote):
        withdrawal_fee = self.withdrawal_fee_quote
        payment_fee = None
        amount_quote += self.withdrawal_fee_quote

        if any([not self.payment_preference, self.pair.is_crypto]):
            return amount_quote, self.withdrawal_fee_quote, payment_fee

        base = Decimal('1.0')
        fee = Decimal('0.0')
        method = self.payment_preference.payment_method
        if self.order_type == self.BUY:
            fee = method.fee_deposit
            if method.pays_deposit_fee == method.MERCHANT:
                fee = -fee
        elif self.order_type == self.SELL:
            fee = self.payment_preference.payment_method.fee_withdraw
            if method.pays_withdraw_fee == method.MERCHANT:
                fee = -fee
        res = amount_quote / (base - fee)
        if res - amount_quote < self.minimal_payment_method_fee_quote:
            res = amount_quote + self.minimal_payment_method_fee_quote
        payment_fee = res - amount_quote
        return res, withdrawal_fee, payment_fee

    @property
    def is_paid(self):
        if self.order_type == self.BUY and not self.exchange:
            return self.is_paid_buy
        elif self.order_type == self.SELL:
            raise NotImplementedError('Exists only for BUY orders.')

    @property
    def success_payments_amount(self):
        payments = self.success_payments_by_reference
        if not payments:
            payments = self.success_payments_by_wallet
        sum_success = Decimal(0)
        for p in payments:
            sum_success += p.amount_cash
        return sum_success

    @property
    def success_payments_by_reference(self):
        ref = self.unique_reference
        payments = Payment.objects.filter(
            is_success=True, reference=ref, currency=self.pair.quote)
        return payments

    @property
    def bad_currency_payments(self):
        payments = self.payment_set.filter(~Q(currency=self.pair.quote))
        return payments

    @property
    def success_payments_by_wallet(self):
        method = self.payment_preference.payment_method
        payments = Payment.objects.filter(
            is_success=True,
            user=self.user,
            amount_cash=self.amount_quote,
            payment_preference__payment_method=method,
            currency=self.pair.quote,
        ).filter(Q(order=self) | Q(order=None))
        return payments

    @property
    def is_paid_buy(self):
        sum_all = self.success_payments_amount
        amount_expected = (
            self.amount_quote -
            self.payment_preference.payment_method.allowed_amount_unpaid
        )
        if sum_all >= amount_expected:
            return True
        return False

    @property
    def part_paid_buy(self):
        if self.order_type != self.BUY:
            return False
        sum_all = self.success_payments_amount
        amount_expected = self.amount_quote
        return sum_all / amount_expected

    @property
    def is_buy(self):
        return self.order_type == self.BUY

    @property
    def requires_withdraw_address(self):
        return (self.order_type == self.BUY) or self.exchange

    @property
    def payment_deadline(self):
        """returns datetime of payment_deadline (creation + payment_window)"""
        # TODO: Use this for pay until message on 'order success' screen
        return self.created_on + timedelta(minutes=self.payment_window)

    @property
    def kyc_deadline(self):
        payment = self.payment_set.last()
        if payment:
            return payment.kyc_void_time

    @property
    def expired(self):
        """Is expired if payment_deadline is exceeded and it's not paid yet"""
        # TODO: validate this business rule
        # TODO: Refactor, it is unreasonable to have different standards of
        # time in the DB
        return (timezone.now() > self.payment_deadline) and \
               (self.status not in Order.IN_PAID + [Order.PAID_UNCONFIRMED])

    @property
    def unpaid_order_expired(self):
        """Is expired when an order has not been payed within 1hr"""
        return (timezone.now() - self.created_on > timedelta(
                minutes=self.unpaid_order_window + self.payment_window)
                ) and (self.status == Order.INITIAL)

    @property
    def payment_status_frozen(self):
        """return a boolean indicating if order can be updated
        Order is frozen if it is expired or has been paid
        """
        # TODO: validate this business rule
        return self.expired or self.status in Order.IN_RELEASED

    @property
    def target(self):
        """return target currency"""
        if self.order_type == self.BUY:
            return self.pair.base.code
        else:
            return self.pair.quote.code

    @property
    def withdrawal_address_frozen(self):
        """return bool whether the withdraw address can
           be changed"""
        return self.status in Order.IN_RELEASED

    @property
    def rate(self):
        return money_format(self._rate, places=8)

    @property
    def inverted_rate(self):
        return money_format(self.amount_base / self.amount_quote, places=8)

    @classmethod
    def pending_amount_diff(cls, currency, additional_amount=0):
        if not isinstance(additional_amount, Decimal):
            additional_amount = Decimal(str(additional_amount))
        return additional_amount
        # FIXME: only use additional_amount for now, logic down is for reading order list  # noqa
        # if not isinstance(currency, Currency):
        #     currency = Currency.objects.get(code=currency)
        # if not isinstance(additional_amount, Decimal):
        #     additional_amount = Decimal(str(additional_amount))
        # base = cls.objects.filter(
        #     pair__base=currency, status=cls.PAID_UNCONFIRMED).aggregate(
        #     Sum('amount_base'))['amount_base__sum']
        # quote = cls.objects.filter(
        #     pair__quote=currency, status=cls.PAID_UNCONFIRMED).aggregate(
        #     Sum('amount_quote'))['amount_quote__sum']
        # if not base:
        #     base = Decimal('0')
        # base += additional_amount
        # if not quote:
        #     quote = Decimal('0')
        # return base - quote

    @classmethod
    def get_current_slippage(cls, currency, quote, additional_amount=0):
        slippage_rate = \
            currency.slippage_rate * quote.quote_slippage_rate_multiplier  # noqa
        unslippaged_amount = \
            currency.unslippaged_amount * quote.quote_unslippaged_amount_multiplier  # noqa
        if not isinstance(currency, Currency):
            currency = Currency.objects.get(code=currency)
        diff = cls.pending_amount_diff(
            currency, additional_amount=additional_amount
        )
        if diff < unslippaged_amount:
            return Decimal('0')
        else:
            return (diff - unslippaged_amount) * slippage_rate  # noqa

    def get_profile(self):
        return self.user.profile

    def notify(self):
        profile = self.get_profile()

        # Activate translation
        if any([profile.notify_by_email, profile.notify_by_phone]):
            activate(profile.lang)

        status_verb = self.get_status_display().lower()
        if self.status == self.INITIAL:
            status_verb = 'created'
        elif self.status == self.PAID_UNCONFIRMED:
            status_verb = 'paid(waiting for confirmation)'

        title = _(
            'Nexchange: Order {ref} {status_verb}'.format(
                ref=self.unique_reference,
                status_verb=status_verb
            )
        )
        msg = _('Your order {ref} is {status_verb}.').format(
            ref=self.unique_reference,
            status_verb=status_verb
        )
        if self.withdraw_address:
            msg += ' Withdraw address: {withdraw_address}.'.format(
                withdraw_address=self.withdraw_address.address
            )
            withdraw_transaction = self.transactions.filter(
                type=Transaction.WITHDRAW).last()
            if withdraw_transaction is not None:
                tx_id = withdraw_transaction.tx_id
                if tx_id is not None:
                    msg += ' Transaction id: {tx_id}.'.format(
                        tx_id=tx_id)
        if self.status == self.RELEASED and self.suggest_trustpilot:
            msg += ' You can rate our service on Trustpilot ' \
                   'https://www.trustpilot.com/review/n.exchange'

        # send sms depending on notification settings in profile
        if profile.notify_by_phone and profile.phone:
            phone_to = str(profile.phone)
            send_sms(msg, phone_to)

        # send email
        if profile.notify_by_email and profile.user.email:
            send_email(profile.user.email, title, msg)

    def __str__(self):
        name = \
            '{status} {type} {unique_reference} {pair} {amount_base} {base} ' \
            '{amount_quote} {quote} {username}'.format(
                username=self.user.username,
                type=self.get_order_type_display(),
                pair=self.pair.name,
                amount_base=str(self.amount_base).rstrip('0').rstrip('.'),
                amount_quote=str(self.amount_quote).rstrip('0').rstrip('.'),
                base=self.pair.base.code,
                quote=self.pair.quote.code,
                status=self.get_status_display(),
                unique_reference=self.unique_reference
            )
        return name

    def calculate_order(self, amount_quote, payment_method=None):
        if self.expired:
            price = Price.objects.filter(
                pair=self.pair, market__is_main_market=True).latest('id')
            now = timezone.now()
            if now > self.payment_deadline:
                expired_minutes = ceil(
                    (now - self.payment_deadline).seconds / 60)
            else:
                expired_minutes = 0
            self.payment_window += settings.PAYMENT_WINDOW + expired_minutes
        else:
            price = self.price
        new_payment_method = False
        if payment_method:
            if not self.payment_preference:
                new_payment_method = True
            elif self.payment_preference.payment_method != payment_method:
                new_payment_method = True

        if any([self.expired, self.amount_quote != amount_quote,
                new_payment_method]):
            if new_payment_method:
                self.payment_preference = PaymentPreference.objects.get(
                    user__is_staff=True,
                    payment_method=payment_method
                )
            self.amount_quote = amount_quote
            self.calculate_base_from_quote(price=price)
            self.save()

    @transition(field=status, source=INITIAL, target=PAID_UNCONFIRMED)
    def _register_deposit(self, tx_data, crypto=True):
        if crypto:
            model = Transaction
            amount_key = 'amount'
        else:
            model = Payment
            amount_key = 'amount_cash'
        order = tx_data.get('order')
        tx_type = tx_data.get('type')
        tx_amount = tx_data.get(amount_key)
        tx_currency = tx_data.get('currency')
        if order.pair.quote.code == 'XRP':
            tx_destination_tag = tx_data.get('destination_tag')
            if order.destination_tag != tx_destination_tag:
                raise ValidationError(
                    'Bad tx destination tag {}. Should be {}'.format(
                        order, self
                    )
                )
        if order.pair.quote.code == 'XMR':
            tx_payment_id = tx_data.get('payment_id')
            if order.payment_id != tx_payment_id:
                raise ValidationError(
                    'Bad tx payment id {}. Should be {}'.format(
                        order, self
                    )
                )

        if order != self:
            raise ValidationError(
                'Bad order {} on the deposit tx. Should be {}'.format(
                    order, self
                )
            )
        if self.pair.quote != tx_currency:
            raise ValidationError(
                'Bad tx currency {}. Order quote currency {}'.format(
                    tx_currency, self.pair.quote
                )
            )
        if tx_type != Transaction.DEPOSIT:
            raise ValidationError(
                'Order {}. Cannot register DEPOSIT - wrong transaction '
                'type {}'.format(self, tx_type))

        if not tx_amount:
            raise ValidationError(
                'Order {}. Cannot register DEPOSIT - bad amount - {}'.format(
                    self, tx_amount))

        # Transaction is created before calculate_order to assure that
        # it will not be hanging (waiting for better rate).

        tx = model(**tx_data)
        tx.save()
        payment_method = None
        if not crypto:
            payment_method = tx.payment_preference.payment_method

        self.calculate_order(tx_amount, payment_method=payment_method)
        self.set_as_paid_unconfirmed_on = timezone.now()

        return tx

    def register_deposit(self, tx_data, crypto=True):
        self.refresh_from_db()
        res = {'status': 'OK'}
        try:
            tx = self._register_deposit(tx_data, crypto=crypto)
            res.update({'tx': tx})
        except Exception as e:
            res = {'status': 'ERROR', 'message': '{}'.format(e)}
            self.refresh_from_db()
        self.save()
        return res

    @transition(field=status, source=PAID_UNCONFIRMED, target=PAID)
    def _confirm_deposit(self, tx, crypto=True):
        if crypto:
            success_params = ['is_completed', 'is_verified']
        else:
            success_params = ['is_complete']
        if tx.type != Transaction.DEPOSIT:
            raise ValidationError(
                'Order {}. Cannot confirm DEPOSIT - wrong transaction '
                'type {}'.format(self, tx.type))
        for param in success_params:
            if getattr(tx, param):
                raise ValidationError(
                    'Order {}.Cannot confirm DEPOSIT - already confirmed({}).'
                    ''.format(self, param)
                )
            setattr(tx, param, True)
        tx.save()
        self.set_as_paid_on = timezone.now()

    def confirm_deposit(self, tx, crypto=True):
        self.refresh_from_db()
        res = {'status': 'OK'}
        try:
            self._confirm_deposit(tx, crypto=crypto)
        except Exception as e:
            res = {'status': 'ERROR', 'message': '{}'.format(e)}
        self.save()
        return res

    @transition(field=status, source=PAID, target=PRE_RELEASE)
    def _pre_release(self, api=None):
        healthy = api.health_check(self.pair.base)
        if not healthy:
            raise ValidationError(_(
                'Wallet {} isin\'t working before the release'.format(
                    self.pair.base.wallet
                )
            ))
        return healthy

    def pre_release(self, api=None):
        self.refresh_from_db()
        res = {'status': 'OK'}
        try:
            self._pre_release(api=api)
        except Exception as e:
            res = {'status': 'ERROR', 'message': '{}'.format(e)}
        self.save()
        return res

    @transition(field=status, source=PRE_RELEASE, target=RELEASED)
    def _release(self, tx_data, api=None, currency=None, amount=None):
        if currency != self.pair.base or amount != self.amount_base:
            raise ValidationError(
                'Wrong amount {} or currency {} for order {} release'.format(
                    amount, currency, self.unique_reference
                )
            )
        old_withdraw_txs = self.transactions.exclude(
            type__in=[Transaction.DEPOSIT, Transaction.INTERNAL])
        tx_type = tx_data.get('type')
        if tx_type != Transaction.WITHDRAW:
            msg = 'Bad Transaction type'
            raise ValidationError(msg)
        if len(old_withdraw_txs) == 0:
            tx = Transaction(**tx_data)
            tx.save()

            payment_id = self.payment_id \
                if self.payment_id is not None \
                else None
            destination_tag = self.destination_tag if \
                self.destination_tag is not None \
                else None
            api.assert_tx_unique(currency, self.withdraw_address, amount,
                                 payment_id=payment_id,
                                 destination_tag=destination_tag)
            tx_id, success = api.release_coins(currency, self.withdraw_address,
                                               amount, payment_id=payment_id,
                                               destination_tag=destination_tag)
            setattr(tx, api.TX_ID_FIELD_NAME, tx_id)
            tx.save()
        else:
            msg = 'Order {} already has WITHDRAW or None type ' \
                  'transactions {}'.format(self, old_withdraw_txs)
            self.flag(val=msg)
            raise ValidationError(msg)

        if not tx_id:
            msg = 'Payment release returned None, order {}'.format(self)
            self.flag(val=msg)
            raise ValidationError(msg)

        if success:
            tx.is_verified = True
            tx.save()
        else:
            app.send_task(self.RETRY_RELEASE, [tx.pk],
                          countdown=settings.RETRY_RELEASE_TIME)

        self.set_as_released_on = timezone.now()
        return tx

    def release(self, tx_data, api=None):
        self.refresh_from_db()
        res = {'status': 'OK'}
        try:
            currency = tx_data.get('currency')
            amount = tx_data.get('amount')
            tx = self._release(tx_data, api=api, currency=currency,
                               amount=amount)
            res.update({'tx': tx})
        except Exception as e:
            msg = '{}'.format(e)
            res = {'status': 'ERROR', 'message': msg}
            self.flag(val=msg)
        self.save()
        return res

    @transition(field=status, source=RELEASED, target=COMPLETED)
    def _complete(self, tx):
        if tx.type != Transaction.WITHDRAW:
            raise ValidationError(
                'Order {}. Cannot confirm WITHDRAW - wrong transaction '
                'type'.format(self))
        if tx.is_completed:
            raise ValidationError(
                'Order {}.Cannot confirm DEPOSIT - already confirmed.'.format(
                    self))
        tx.is_completed = True
        tx.is_verified = True
        tx.save()

    def complete(self, tx):
        self.refresh_from_db()
        res = {'status': 'OK'}
        try:
            self._complete(tx)
        except Exception as e:
            res = {'status': 'ERROR', 'message': '{}'.format(e)}
        self.save()
        return res

    @transition(field=status, source=PAID_UNCONFIRMED, target=CANCELED)
    @transition(field=status, source=INITIAL, target=CANCELED)
    def _cancel(self):
        pass

    def cancel(self):
        self.refresh_from_db()
        res = {'status': 'OK'}
        try:
            self._cancel()
        except Exception as e:
            msg = '{}'.format(e)
            res = {'status': 'ERROR', 'message': msg}
            self.flag(val=msg)
        self.save()
        return res

    @transition(field=status, source=PRE_RELEASE, target=REFUNDED)
    @transition(field=status, source=PAID, target=REFUNDED)
    def _refund_exchange(self):
        old_withdraw_txs = self.transactions.exclude(
            type__in=[Transaction.DEPOSIT, Transaction.INTERNAL])
        dep_tx = self.transactions.get(type='D')
        dep_tx.flag(val='refunded')
        currency = dep_tx.currency
        amount = dep_tx.amount
        address = self.refund_address
        if currency != self.pair.quote:
            msg = 'DEPOSIT tx currency {} is not the same as order quote {}.' \
                  ' Order {}'.format(dep_tx.currency, self.pair.quote,
                                     self.unique_reference)
            self.flag(val=msg)
            raise ValidationError(msg)
        if amount != self.amount_quote:
            msg = 'DEPOSIT tx amount {} is not the same as order ' \
                  'amount_quote {}. Order {}'.format(amount, self.amount_quote,
                                                     self.unique_reference)
            self.flag(val=msg)
            raise ValidationError(msg)
        if any([not address, address.currency != self.pair.quote,
                address == self.deposit_address]):
            msg = 'Wrong refund address {}.Order {}'.format(
                address, self.unique_reference
            )
            self.flag(val=msg)
            raise ValidationError(msg)
        if len(old_withdraw_txs) == 0 and dep_tx:
            tx = Transaction(
                amount=amount,
                type=Transaction.REFUND,
                currency=currency,
                refunded_transaction=dep_tx,
                address_to=address,
                order=self
            )
            tx.save()
            api = factory.get_api_client(currency.wallet)
            tx_id, success = api.release_coins(currency, address, amount)
            setattr(tx, api.TX_ID_FIELD_NAME, tx_id)
            tx.save()
        else:
            msg = 'Order {} already has WITHDRAW or None type ' \
                  'transactions {}'.format(self, old_withdraw_txs)
            self.flag(val=msg)
            raise ValidationError(msg)

        return tx

    @transition(field=status, source=PAID_UNCONFIRMED, target=REFUNDED)
    def _refund_fiat(self, refund_type='void'):
        payment = self.payment_set.get()
        payment.flag(val=refund_type)
        if not payment.is_success:
            msg = 'Cannot refund order {}. Deposit payment {} is not ' \
                  'successful'.format(self.unique_reference, payment)
            self.flag(val=msg)
            raise ValidationError(msg)
        order = payment.order
        if order != self:
            msg = 'Cannot refund order {}. Payment order {} is not the ' \
                  'same'.format(self.unique_reference, order)
            self.flag(val=msg)
            raise ValidationError(msg)
        params = {
            'currency': payment.currency.code,
            'amount': str(money_format(payment.amount_cash, places=2)),
            'clientRequestId': '{}_{}'.format(refund_type,
                                              order.unique_reference),
            'clientUniqueId': '{}_{}'.format(refund_type,
                                             order.unique_reference),
            'relatedTransactionId': payment.secondary_payment_system_id,
            'authCode': payment.auth_code
        }
        if refund_type == 'refund':
            res = safe_charge_client.api.refundTransaction(**params)
        elif refund_type == 'void':
            res = safe_charge_client.api.voidTransaction(**params)
        status = res.get('transactionStatus', '')
        if status != 'APPROVED':
            msg = 'Cannot refund order {}. Attemp is not approved: {}'.format(
                self.unique_reference, res
            )
            self.flag(val=msg)
            raise ValidationError(msg)
        payment.is_success = False
        payment.save()
        payment.flag(val=res)

    def refund(self, refund_type='void'):
        self.refresh_from_db()
        res = {'status': 'OK'}
        try:
            if self.exchange:
                tx = self._refund_exchange()
                res.update({'tx': tx})
            else:
                self._refund_fiat(refund_type=refund_type)
        except Exception as e:
            res = {'status': 'ERROR', 'message': '{}'.format(e)}
        self.save()
        return res

    @property
    def amount_quote_fee(self):
        if not self.payment_preference:
            return Decimal('0.0')
        fee = self.payment_preference.payment_method.fee_deposit
        return fee * self.amount_quote

    @property
    def token(self):
        order_count = self.user.orders.all().count()
        if order_count > 1:
            return
        try:
            token = AccessToken.objects.filter(user=self.user).latest('id')
        except AccessToken.DoesNotExist:
            return
        if not token or not token.is_valid():
            return
        return token.token

    @property
    def display_refund_address(self):
        if self.status == self.PRE_RELEASE:
            return True
        if self.status == self.PAID:
            if self.set_as_paid_on \
                    and self.set_as_paid_on + \
                    settings.MAX_TIME_TO_RELEASE_INTERVAL < timezone.now():
                return True
        return False

    @property
    def base_destination_tag(self):
        if self.pair.base.code == 'XRP':
            return self.destination_tag

    @property
    def quote_destination_tag(self):
        if self.pair.quote.code == 'XRP':
            return self.destination_tag

    @property
    def base_payment_id(self):
        if self.pair.base.code == 'XMR':
            return self.payment_id

    @property
    def quote_payment_id(self):
        if self.pair.quote.code == 'XMR':
            return self.payment_id

    @property
    def _referred_with(self):
        try:
            return self.user.profile.referred_with
        except ObjectDoesNotExist:
            pass

    @property
    def fees(self):
        return self.orderfee_set.all()

    @property
    def payment_to_release_time(self):
        if self.set_as_paid_unconfirmed_on and self.set_as_released_on:
            return self.set_as_released_on - self.set_as_paid_unconfirmed_on

    @property
    def create_to_release_time(self):
        if self.created_on and self.set_as_released_on:
            return self.set_as_released_on - self.created_on

    @property
    def is_released_fast(self):
        if self.payment_to_release_time:
            tot_pay_release = self.payment_to_release_time.total_seconds()
            fast_pay_release = \
                tot_pay_release < settings.FAST_PAYMENT_TO_RELEASE_TIME_SECONDS
            tot_create_release = self.create_to_release_time.total_seconds()
            fast_create_release = tot_create_release < settings.\
                FAST_CREATE_TO_RELEASE_TIME_SECONDS
            return fast_create_release and fast_pay_release
        return False

    @property
    def suggest_trustpilot(self):
        user_orders = self.user.orders.all()
        if self.is_released_fast and not user_orders.filter(
                status__in=self.REFUNDABLE_STATES + [self.REFUNDED]):
            for _order in user_orders.filter(
                    status__in=self.IN_SUCCESS_RELEASED):
                if not _order.is_released_fast:
                    return False
            return True
        return False

    @property
    def amount_quote_minus_fees(self):
        tot_fees = self.orderfee_set.all().aggregate(
            models.Sum('amount_quote')
        ).get('amount_quote__sum')
        tot_fees = tot_fees if tot_fees else Decimal('0')
        return self.amount_quote - tot_fees


from flask import Flask, request

app = Flask(__name__)
app.config["DEBUG"] = True

@app.after_request
def enableCors(response):
	response.headers.add('Access-Control-Allow-Origin', '*')
	response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
	response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS')
	return response

def shutdown_server():
	func = request.environ.get('werkzeug.server.shutdown')
	if func is None:
		raise RuntimeError('Not running with the Werkzeug Server')
	func()

@app.route('/__kill__', methods=['POST'])
def shutdown():
	shutdown_server()
	return 'Server shutting down...'

import api.services


import math
def encryption(s):
	s = ''.join(s.split(" "))
	l = len(s)
	rows = math.floor(l**(1/2))
	cols = math.ceil(l**(1/2))
	arr1 = []
	end = 0
	while  end <= l:
		arr1.append(s[end:end+cols])
		end = end+cols
	resObj = {}
	for v1 in arr1:
		for i,v2 in enumerate(v1):
			print(i,v2)
			resObj[i] = resObj.get(i,'')+v2
	return " ".join(resObj.values())
if __name__ == '__main__':
    result = encryption("feedthedog")
    print(result,"result")


# Напишите программу, которая будет преобразовывать десятичное число в двоичное. Нельзя использовать готовые функции.
def bin_num(dem, bin):
    return dem, bin
try:
    x = int(input('Введите целое число: '))
    y = ''
    n = x
    while x > 0:
        z = str(x % 2)
        y = z + y
        x = int(x / 2)
    print(bin_num(n, y))
except:
    print('Введены некорректные значения')


import numpy as np
from linear_loss_reg import reg_loss_

def main():
	y = np.array([2, 14, -13, 5, 12, 4, -19]).reshape((-1, 1))
	y_hat = np.array([3, 13, -11.5, 5, 11, 5, -20]).reshape((-1, 1))
	theta = np.array([1, 2.5, 1.5, -0.9]).reshape((-1, 1))

	# Example 1:
	result = reg_loss_(y, y_hat, theta, .5)
	print(f'Example 1: {result}\n')
	assert np.isclose(result, 0.8503571428571429)

	# Example 2:
	result = reg_loss_(y, y_hat, theta, .05)
	print(f'Example 2: {result}\n')
	assert np.isclose(result, 0.5511071428571429)

	# Example 3:
	result = reg_loss_(y, y_hat, theta, .9)
	print(f'Example 3: {result}\n')
	assert np.isclose(result, 1.116357142857143)

if __name__ == '__main__':
	main()


from collections import deque
class Node:
    def __init__(self,data):
        self.data=data
        self.lchild=None
        self.rchild=None

class Tree:
    def __init__(self):
        self.root=None

    def create(self):
        print("Enter the root data")
        data=input()
        rootnode=Node(data)
        self.root=rootnode
        list=deque()
        list.append(self.root)

        while len(list)!=0:
            p=list.popleft()
            print("Enter the left child to"+str(p.data)+"or -1 to pass")
            x=input()
            if(x!='-1'):
                t1=Node(x)
                p.lchild=t1
                list.append(t1)
                print(list)

            print("Enter the right child to"+str(p.data)+" or -1 to pass")
            x = input()
            if (x !='-1'):
                t2 = Node(x)
                p.rchild = t2
                list.append(t2)
                print(list)

    def preorder(self):
        if self.root is None:
            print("Empty Tree")
        else:
            self.preorderstart(self.root)
    def preorderstart(self,p):
        if p is not None:
            print(p.data)
            self.preorderstart(p.lchild)
            self.preorderstart(p.rchild)

    def inorder(self):
        if self.root is None:
            print("Empty Tree")
        else:
            self.inorderstart(self.root)
    def inorderstart(self,p):
        if p is not None:
            self.inorderstart(p.lchild)
            print(p.data)
            self.inorderstart(p.rchild)

    def postorder(self):
        if self.root is None:
            print("Empty Tree")
        else:
            self.postorderstart(self.root)
    def postorderstart(self,p):
        if p is not None:
            self.postorderstart(p.lchild)
            self.postorderstart(p.rchild)
            print(p.data)

    def levelorderiter(self):
        queue = deque()
        p = self.root
        queue.append(p)

        while queue:
            p = queue.popleft()
            print(p.data)
            if p.lchild is not None:
                queue.append(p.lchild)
            if p.rchild is not None:
                queue.append(p.rchild)

    def count(self):
        if self.root is None:
            return 0
        else:
            print("(nodes,height)")
            nodes=self.countnodes(self.root)
            height=self.heightcount(self.root)
            return nodes,height
    def heightcount(self,p):
        if p is not None:
            x=self.heightcount(p.lchild)
            y=self.heightcount(p.rchild)
            if (x>y):
                return x+1
            else:
                return y+1
        return 0

    def countnodes(self,p):
            if p is not None:
                x= self.countnodes(p.lchild)
                y=self.countnodes(p.rchild)

                return int(x+y+1)
            else:
                return 0

if __name__ == '__main__':
    tree=Tree()
    root=Node(8)
    root.lchild=Node(3)
    root.lchild.lchild=Node(4)
    root.lchild.rchild=Node(9)
    root.rchild=Node(5)
    root.rchild.lchild=Node(7)
    root.rchild.rchild=Node(2)

    tree.root=root
    tree.preorder()


print("Working...")
from tensorflow.keras.models import load_model
from tensorflow.keras.models import model_from_json

import cv2
import numpy as np
import argparse
import logging
import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'  # FATAL
logging.getLogger('tensorflow').setLevel(logging.FATAL)

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
	help="path to test image")
args = vars(ap.parse_args())

model_architecture = 'architecture.json'
model_weights = 'weights.h5'

json_file = open('architecture.json', 'r')
loaded_model_json = json_file.read()
json_file.close()
model = model_from_json(loaded_model_json)
# load weights into new model
model.load_weights("weights.h5")

print("[INFO] Evaluating network...")
img = cv2.imread(args["image"])
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (224, 224))

predIdxs = model.predict(np.array([img,])/255.0, batch_size=1)[0]

if predIdxs[0] > predIdxs[1]:
	print("COVID-19 POSITIVE IMAGE")
else:
	print("NORMAL IMAGE")


import torch
import torch.nn as nn

class EEGNet(nn.Module):
    def __init__(self, nb_classes=2, alpha=1.0, p=0.25):
        super(EEGNet, self).__init__()

        # Set up the hyperparameters
        self.nb_classes = nb_classes
        self.firstconv = nn.Sequential(
            nn.Conv2d(1, 16, (1, 51), (1, 1), (0, 25), bias=False),
            nn.BatchNorm2d(16)
        )

        self.depthwiseConv = nn.Sequential(
            nn.Conv2d(16, 32, (2, 1), (1, 1), groups=16, bias=False),
            nn.BatchNorm2d(32),
            nn.ELU(alpha),
            nn.AvgPool2d((1, 4), (1, 4)),
            nn.Dropout(p),
        )

        self.separableConv = nn.Sequential(
            nn.Conv2d(32, 32, (1, 15), (1, 1), (0, 7), bias=False),
            nn.BatchNorm2d(32),
            nn.ELU(alpha),
            nn.AvgPool2d((1, 8), (1, 8)),
            nn.Dropout(p),
        )

        self.classify = nn.Sequential(
            nn.Linear(in_features=736, out_features=2)
        )

        # Initialize the weights
        self._init_weights()

    def _init_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.xavier_uniform_(m.weight, gain=1.0)
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.xavier_uniform_(m.weight, gain=1.0)
                nn.init.constant_(m.bias, 0)

    def forward(self, x):
        x = self.firstconv(x)
        x = self.depthwiseConv(x)
        x = self.separableConv(x)
        x = x.view(x.size(0), -1)
        x = self.classify(x)
        return x

if __name__ == "__main__":
    model = EEGNet(nb_classes=2)
    print(model)


'''
 # @ Author: Bao Loc Pham
 # @ Create Time: 2023-06-09 16:09:22
 # @ Modified by: Bao Loc Pham
 # @ Modified time: 2023-06-10 10:11:30
 # @ Description: This file is for testing churn library
 '''

import os
import logging
import warnings
import churn_library as clb
# import pytest
warnings.filterwarnings("ignore")

logging.basicConfig(
    filename='./logs/churn_library.log',
    level=logging.DEBUG,
    filemode='w',
    format='%(name)s - %(levelname)s - %(message)s')

# @pytest.fixture(scope="module")
# def response_str():
#     """
#     pytest Fixture response_str
#     """
#     return "Churn"

# @pytest.fixture(scope="module")
# def path():
#     """
#     pytest Fixture path
#     """
#     return "./data/bank_data.csv"

# @pytest.fixture(scope="module")
# def data(path):
#     """
#     pytest Fixture path
#     """
#     return clb.import_data(path)

# @pytest.fixture(scope="module")
# def train_test_data(data, response_str):
#     """
#     pytest Fixture train_test_data
#     """
#     return clb.perform_feature_engineering(data, response_str)

# @pytest.fixture(scope="module")
# def eda_plot_paths():
#     """
#     pytest Fixture eda_plot_paths
#     """
#     return [
#         r"images/eda/churn_distribution.png",
#         r"images/eda/customer_age_distribution.png",
#         r"images/eda/heatmap.png",
#         r"images/eda/marital_status_distribution.png",
#         r"images/eda/total_trans__distribution.png",
#     ]

# @pytest.fixture(scope="module")
# def train_plot_paths():
#     """
#     pytest Fixture train_plot_paths
#     """
#     return [
#         r"images/results/feature_importances.png",
#         r"images/results/logistic_regression_classification_report.png",
#         r"images/results/logistic_resuts.png",
#         r"images/results/random_forest_classification_report.png",
#         r"images/results/roc_curve_results.png",
#         r"images/results/shap_summary.png",
#     ]

# @pytest.fixture(scope="module")
# def train_model_paths():
#     """
#     pytest Fixture train_model_paths
#     """
#     return [
#         r"models/logistic_model.pkl",
#         r"models/rfc_model.pkl",
#     ]

# @pytest.fixture(scope="module")
# def category_list():
#     """
#     pytest Fixture category_list
#     """
#     return [
#         'Gender',
#         'Education_Level',
#         'Marital_Status',
#         'Income_Category',
#         'Card_Category'
#     ]

# @pytest.fixture(scope="module")
# def keep_cols_list():
#     """
#     pytest Fixture keep_cols_list
#     """
#     return [
#         'Customer_Age',
#         'Dependent_count',
#         'Months_on_book',
#         'Total_Relationship_Count',
#         'Months_Inactive_12_mon',
#         'Contacts_Count_12_mon',
#         'Credit_Limit',
#         'Total_Revolving_Bal',
#         'Avg_Open_To_Buy',
#         'Total_Amt_Chng_Q4_Q1',
#         'Total_Trans_Amt',
#         'Total_Trans_Ct',
#         'Total_Ct_Chng_Q4_Q1',
#         'Avg_Utilization_Ratio',
#         'Gender_Churn',
#         'Education_Level_Churn',
#         'Marital_Status_Churn',
#         'Income_Category_Churn',
#         'Card_Category_Churn']

def test_import_data(path):
	'''
	test data import - this example is completed for you to assist with the other test functions
	'''
	logging.info("Testing import_data")
	try:
		dataframe = clb.import_data(path)
		logging.info("Testing import_data: SUCCESS")
	except FileNotFoundError as err:
		logging.error("Testing import_eda: The file wasn't found")
		raise err

	try:
		assert len(dataframe) > 0
		assert len(dataframe.columns) > 0
	except AssertionError as err:
		logging.error(
			"Testing import_data: The file doesn't appear to have rows and columns")
		raise err
	logging.info("SUCCES test import_data")

def test_perform_eda(data, eda_plot_paths):
	'''
	test perform eda function
	'''
	logging.info("Testing perform_eda")
	try:
		clb.perform_eda(data)
		for plot_path in eda_plot_paths:
			plot_path = os.path.join(os.getcwd(), plot_path)
			assert os.path.exists(plot_path)
	except AssertionError as err:
		logging.error("Test perform_eda: file not found")
		raise err
	logging.info("SUCCES test perform_eda")

def test_encoder_helper(data, category_list, response_str):
	'''
	test encoder helper
	'''
	logging.info("Testing encoder_helper")
	try:
		encoded_data = clb.encoder_helper(data, category_list, response_str)
		assert len(encoded_data.columns) - \
			len(category_list) != len(data.columns)
	except AssertionError as err:
		logging.error(
			"Test encoder_helper: encoded_data doesn't have enough encoded colums")
		raise err
	logging.info("SUCCES test encoder_helper")

def test_perform_feature_engineering(data, response_str, keep_cols_list):
	'''
	test perform_feature_engineering
	'''
	logging.info("Testing perform_feature_engineering")
	try:
		x_train, x_test, y_train, y_test = clb.perform_feature_engineering(
			data, response_str)
		assert x_train.shape[0] > 0
		assert x_test.shape[0] > 0
		assert y_train.shape[0] > 0
		assert y_test.shape[0] > 0

		assert (x_train.columns == keep_cols_list).all
		assert (x_test.columns == keep_cols_list).all
	except AssertionError as err:
		logging.error(
			"Test perform_feature_engineering: Error in shape, column assertion")
		raise err
	logging.info("SUCCES test perform_feature_engineering")

def test_train_models(train_test_data, train_plot_paths, train_model_paths):
	'''
	test train_models
	'''
	logging.info("Testing train_models")
	try:
		x_train, x_test, y_train, y_test = train_test_data
		clb.train_models(x_train, x_test, y_train, y_test)

		for plot_path in train_plot_paths:
			assert os.path.exists(plot_path)

		for model_path in train_model_paths:
			assert os.path.exists(model_path)

	except AssertionError as err:
		logging.error("Test encoder_helper: Eror in file existance assertion")
		raise err
	logging.info("SUCCES test train_models")

if __name__ == "__main__":
    PATH_DATA = "./data/bank_data.csv"
    test_import_data(PATH_DATA)
    EDA_PLOT_PATHS = [
        r"images/eda/churn_distribution.png",
        r"images/eda/customer_age_distribution.png",
        r"images/eda/heatmap.png",
        r"images/eda/marital_status_distribution.png",
        r"images/eda/total_trans_distribution.png",
    ]
    DATA = clb.import_data(PATH_DATA)
    test_perform_eda(DATA, EDA_PLOT_PATHS)
    CATEGORY_LIST = [
        'Gender',
        'Education_Level',
        'Marital_Status',
        'Income_Category',
        'Card_Category'
    ]
    KEEP_COLS_LIST = [
        'Customer_Age',
        'Dependent_count',
        'Months_on_book',
        'Total_Relationship_Count',
        'Months_Inactive_12_mon',
        'Contacts_Count_12_mon',
        'Credit_Limit',
        'Total_Revolving_Bal',
        'Avg_Open_To_Buy',
        'Total_Amt_Chng_Q4_Q1',
        'Total_Trans_Amt',
        'Total_Trans_Ct',
        'Total_Ct_Chng_Q4_Q1',
        'Avg_Utilization_Ratio',
        'Gender_Churn',
        'Education_Level_Churn',
        'Marital_Status_Churn',
        'Income_Category_Churn',
        'Card_Category_Churn']
    RESPONSE_STR = "Churn"
    TRAIN_PLOT_PATHS = [
        r"images/results/feature_importances.png",
        r"images/results/logistic_regression_classification_report.png",
        r"images/results/logistic_resuts.png",
        r"images/results/random_forest_classification_report.png",
        r"images/results/roc_curve_results.png",
        r"images/results/shap_summary.png",
    ]
    TRAIN_MODEL_PATHS = [
        r"models/logistic_model.pkl",
        r"models/rfc_model.pkl",
    ]
    test_encoder_helper(DATA, CATEGORY_LIST, RESPONSE_STR)
    test_perform_feature_engineering(DATA, RESPONSE_STR, KEEP_COLS_LIST)
    TRAIN_TEST_DATA = clb.perform_feature_engineering(DATA, RESPONSE_STR)
    test_train_models(TRAIN_TEST_DATA, TRAIN_PLOT_PATHS, TRAIN_MODEL_PATHS)


"""
This is a template algorithm on Quantopian for you to adapt and fill in.
"""
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import AverageDollarVolume, CustomFactor, Returns
from quantopian.pipeline.filters import StaticAssets
from quantopian.pipeline.filters.morningstar import Q1500US

def logging(msgs):
    dt = get_datetime('US/Eastern')
    youbi = dt.strftime("%w")
    youbidict = {0:"Sun", 1:"Mon", 2:"Tue", 3:"Wed", 4:"Thu", 5:"Fri", 6:"Sat"}
    msgs = '\t%s\t%s:\t%s' % (dt.strftime('%Y/%m/%d %H:%M'), youbidict[int(youbi)], msgs)
    log.info(msgs)

def initialize(context):
    """
    Called once at the start of the algorithm.
    """
    context.vxx = sid(38054)
    context.tvix = sid(40515)
    context.xiv = sid(40516)
    context.forward = sid(8554)
    context.inverse = sid(32382)

    # Rebalance every day, 1 hour after market open.
    schedule_function(my_rebalance_open, date_rules.every_day(), time_rules.market_open(minutes=60))

    schedule_function(my_rebalance_close, date_rules.every_day(), time_rules.market_close())

    set_commission(commission.PerShare(cost=0, min_trade_cost=0))
    set_slippage(slippage.FixedSlippage(spread=0))
    # Record tracking variables at the end of each day.
    #schedule_function(my_record_vars, date_rules.every_day(), time_rules.market_close())

    # Create our dynamic stock selector.
    attach_pipeline(make_pipeline(), 'my_pipeline')

class PrevClose(CustomFactor):
    inputs = [USEquityPricing.close]
    window_length = 2
    def compute(self, today, assets, out, close):
        out[:] = close[-2]

def make_pipeline():
    """
    A function to create our dynamic stock selector (pipeline). Documentation on
    pipeline can be found here: https://www.quantopian.com/help#pipeline-title
    """

    sids = StaticAssets(symbols('VXX', 'XIV', 'TVIX'))
    base_universe = sids

    # Factor of yesterday's close price.
    yesterday_close = PrevClose()
    close_to_close = Returns(window_length=2)

    pipe = Pipeline(
        screen = base_universe,
        columns = {
            'yesterday_close': yesterday_close,
            'close_to_close': close_to_close,

        }
    )
    return pipe

def before_trading_start(context, data):
    """
    Called every day before market open.
    """
    context.output = pipeline_output('my_pipeline')
    context.go = False
    if abs(context.output.ix[context.vxx]['close_to_close']) > 0.01:
        logging("VXX Return: %s" % context.output.ix[context.vxx]['close_to_close'] )
        context.go = True

    # These are the securities that we are interested in trading each day.
    #context.security_list = context.output.index

def my_assign_weights(context, data):
    """
    Assign weights to securities that we want to order.
    """
    pass

def my_rebalance_close(context,data):
    if context.portfolio.positions[context.vxx]:
        if data.can_trade(context.vxx):
            order_target(context.vxx, 0)
    if context.portfolio.positions[context.xiv]:
        if data.can_trade(context.xiv):
            order_target(context.xiv, 0)

    if context.portfolio.positions[context.tvix]:
        if data.can_trade(context.tvix):
            order_target(context.tvix, 0)

def my_rebalance_open(context,data):

    if context.go and data.can_trade(context.vxx) and data.can_trade(context.tvix):
    #if data.can_trade(context.xiv) and data.can_trade(context.tvix):
        order_percent(context.vxx, 0.6)
        order_percent(context.tvix,-0.3)

        #order_percent(context.xiv,-0.6)

def my_record_vars(context, data):
    """
    Plot variables at the end of each day.
    """
    pass

def handle_data(context,data):
    """
    Called every minute.
    """
    track_orders(context, data)

def track_orders(context, data):
    '''  Show orders when made and filled.
           Info: https://www.quantopian.com/posts/track-orders
    '''
    c = context
    if 'trac' not in c:
        c.t_options = {           # __________    O P T I O N S    __________
            'log_neg_cash': 1,    # Show cash only when negative.
            'log_cash'    : 0,    # Show cash values in logging window or not.
            'log_ids'     : 0,    # Include order id's in logging window or not.
            'log_unfilled': 1,    # When orders are unfilled. (stop & limit excluded).
        }    # Move these to initialize() for better efficiency.
        c.trac = {}
        c.t_dates  = {  # To not overwhelm the log window, start/stop dates can be entered.
            'active': 0,
            'start' : [],   # Start dates, option like ['2007-05-07', '2010-04-26']
            'stop'  : []    # Stop  dates, option like ['2008-02-13', '2010-11-15']
        }
    from pytz import timezone     # Python only does once, makes this portable.
                                  #   Move to top of algo for better efficiency.
    # If 'start' or 'stop' lists have something in them, triggers ...
    if c.t_dates['start'] or c.t_dates['stop']:
        date = str(get_datetime().date())
        if   date in c.t_dates['start']:    # See if there's a match to start
            c.t_dates['active'] = 1
        elif date in c.t_dates['stop']:     #   ... or to stop
            c.t_dates['active'] = 0
    else: c.t_dates['active'] = 1           # Set to active b/c no conditions.
    if c.t_dates['active'] == 0: return     # Skip if not active.
    def _minute():   # To preface each line with the minute of the day.
        bar_dt = get_datetime().astimezone(timezone('US/Eastern'))
        return str((bar_dt.hour * 60) + bar_dt.minute - 570).rjust(3) # (-570 = 9:31a)
    def _trac(to_log):      # So all logging comes from the same line number,
        log.info(to_log)    #   for vertical alignment in the logging window.

    for oid in c.trac.copy():               # Existing known orders
      o = get_order(oid)
      c.trac[o.id]['cb'] = c.portfolio.positions[o.sid].cost_basis if c.portfolio.positions[o.sid].cost_basis else c.trac[o.id]['cb']
      if o.dt == o.created: continue        # No chance of fill yet.
      cash = ''
      if (c.t_options['log_neg_cash'] and c.portfolio.cash < 0) or c.t_options['log_cash']:
        cash = 'cash {}'.format(int(c.portfolio.cash))
      if o.status == 2:                     # Canceled
        prc = '%.2f' % data.current(o.sid, 'price') if data.can_trade(o.sid) else 'unknwn'
        do  = 'Buy' if o.amount > 0 else 'Sell' ; style = ''
        if o.stop:
          style = ' stop {}'.format(o.stop)
          if o.limit: style = ' stop {} limit {}'.format(o.stop, o.limit)
        elif o.limit: style = ' limit {}'.format(o.limit)
        _trac(' {}     Canceled {} {} {}{} at {}   {}  {}'.format(_minute(), do, o.amount,
           o.sid.symbol, style, prc, cash, o.id[-4:] if c.t_options['log_ids'] else ''))
        del c.trac[o.id]
      elif o.filled:                        # Filled at least some.
        filled = '{}'.format(o.amount)
        filled_amt = 0
        if o.filled == o.amount:            # Complete
          if 0 < c.trac[o.id]['amnt'] < o.amount:
            filled   = 'all {}/{}'.format(o.filled - c.trac[o.id]['amnt'], o.amount)
          filled_amt = o.filled
        else:                               # ['amnt'] is previously filled total
          filled_amt = o.filled - c.trac[o.id]['amnt']   # filled this time, can be 0
          c.trac[o.id]['amnt'] = o.filled                # save for increments math
          filled = '{}/{}'.format(filled_amt, o.amount)
        if filled_amt:
          prc = c.portfolio.positions[o.sid].last_sale_price if o.sid in c.portfolio.positions else 'unknwn'
          prc = data.history(o.sid, 'price', 1, '1d')[-1] if prc is 'unknwn' else prc
          now = ' ({})'.format(c.portfolio.positions[o.sid].amount) if c.portfolio.positions[o.sid].amount else ' _'
          pnl = ''  # for the trade only
          amt = c.portfolio.positions[o.sid].amount ; style = ''
          if (amt - o.filled) * o.filled < 0:  # Profit-taking scenario including short-buyback
            if c.trac[o.id]['cb']:
              pnl  = -filled_amt * (prc - c.trac[o.id]['cb'])
              sign = '+' if pnl > 0 else '-'
              pnl  = '  ({}{})'.format(sign, '%.0f' % abs(pnl))
          if o.stop:
            style = ' stop {}'.format(o.stop)
            if o.limit: style = ' stop () limit {}'.format(o.stop, o.limit)
          elif o.limit: style = ' limit {}'.format(o.limit)
          if o.filled == o.amount: del c.trac[o.id]
          _trac(' {}      {} {} {}{} at {}{}{}'.format(_minute(),
            'Bot' if o.amount > 0 else 'Sold', filled, o.sid.symbol, now,
            '%.2f' % prc, pnl, style).ljust(52) + '  {}  {}'.format(cash, o.id[-4:] if c.t_options['log_ids'] else ''))
      elif c.t_options['log_unfilled'] and not (o.stop or o.limit):
        _trac(' {}         {} {}{} unfilled  {}'.format(_minute(), o.sid.symbol, o.amount,
         ' limit' if o.limit else '', o.id[-4:] if c.t_options['log_ids'] else ''))

    oo = get_open_orders().values()
    if not oo: return                       # Handle new orders
    cash = ''
    if (c.t_options['log_neg_cash'] and c.portfolio.cash < 0) or c.t_options['log_cash']:
      cash = 'cash {}'.format(int(c.portfolio.cash))
    for oo_list in oo:
      for o in oo_list:
        if o.id in c.trac: continue         # Only new orders beyond this point
        prc  = '%.2f' % data.current(o.sid, 'price') if data.can_trade(o.sid) else 'unknwn'
        c.trac[o.id] = {'amnt': 0, 'cb': 0} ; style = ''
        now  = ' ({})'.format(c.portfolio.positions[o.sid].amount) if c.portfolio.positions[o.sid].amount else ' _'
        if o.stop:
          style = ' stop {}'.format(o.stop)
          if o.limit: style = ' stop {} limit {}'.format(o.stop, o.limit)
        elif o.limit: style = ' limit {}'.format(o.limit)
        _trac(' {}   {} {} {}{} at {}{}'.format(_minute(), 'Buy' if o.amount > 0 else 'Sell',
          o.amount, o.sid.symbol, now, prc, style).ljust(52) + '  {}  {}'.format(cash, o.id[-4:] if c.t_options['log_ids'] else ''))

algorithms/vxx tvix daytime/backtests/Backtest 6-5916bfaf5ed5976620c924fb.py


# -*- coding: utf-8 -*-
# @Time    : 2017/8/8 14:46
# @Author  : LiYun
# @File    : yun_predict_based_on_ffnn_median_assist.py
'''description:
定义一些yun_predict_based_on_ffnn_median.py中使用的函数
'''
import numpy as np
import tensorflow as tf
import ffnn_inference as ffnn1
import ffnn_inference2 as ffnn2

# 每个月有几天
mdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
#2017年每个月第一天是星期几
first_weekday = (7,3,3,6,1,4,6,2,5,7,3,5)
# 输入数据中每行元素的起始位置，分别为
# link的结束位置，月，日，（起始时，分，秒），（终止时，分，秒），通行时间
Lp = [19, 25, 28, 43, 46, 49, 63, 66, 69, 73]
# 2017年3-7月节假日的列表
festerval=[[4,5,11,12,18,19,25,26],
           [2,3,4,8,9,15,16,22,23,29,30],
           [1,6,7,13,14,20,21,28,29,30],
           [3,4,10,11,17,18,24,25],
           [1,2,8,9,15,16,22,23,29,30]]

def get_all_links(linkinfo):
    '''给定link信息文件，返回所有路段的名称与对应序号所构成的字典'''
    Links=[]
    with open(linkinfo) as f:
        f.readline()
        while True:
            line=f.readline()
            if not line:
                break
            Links.append(line[:Lp[0]])
    Links=sorted(Links)
    Links2={}
    Links3={}
    num=0
    for l in Links:
        Links2[l]=num
        Links3[num]=l
        num+=1
    return Links2,Links3

def readdata(infile):
    '''从输入文件中读取数据，将数据存储在列表里并返回，注意输入文件没有表头'''
    data = []
    with open(infile) as f:
        while True:
            line = f.readline()
            if not line:
                break
            data.append(line[:-1])
    return data

def get_day(d,btime):
    '''给定日期，推出这是第几天'''
    if d[:2]==btime[:2]: #相同月份
        return int(d[3:5])-int(btime[3:5])
    else: #不同月份
        a=int(btime[3:5])
        b=int(d[3:5])
        for i in range(int(btime[:2]),int(d[:2])):
            b+=mdays[i-1]
        return b-a

def get_week(d):
    '''给定日期，推出这是周几'''
    weekday = (int(d[3:5]) + first_weekday[int(d[:2]) - 1] - 2) % 7 + 1
    return weekday

def get_workday():
    workday = np.empty(153, np.int8)
    knt=0
    for m in range(3,8):
        for d in range(1,mdays[m-1]+1):
            if d in festerval[m-3]:
                workday[knt]=0
            else:
                workday[knt]=1
            knt+=1
    return workday

def get_slide(t):
    '''给定时间，推出这是第几个时间片'''
    bm=int(t[:2])*60+int(t[3:5])
    em=int(t[6:8])*60+int(t[9:])
    return int((em-bm)/2)

def get_history(data):
    '''得到3 月至6 月每天 05:00 到 10:00 的历史数据
    和7月每天 06:00 到 08:00 的当前数据'''
    his=np.empty((122,150),np.float) #一共是122天，5个小时是5*30=150个时间片，1条Link
    cur=np.empty((31,60),np.float) #一共是31天，60个时间片，1条Link
    for info in data:
        if int(info[Lp[1]:Lp[1] + 2]) == 7:
            if info[Lp[3]:Lp[3] + 2]!='06' and info[Lp[3]:Lp[3] + 2]!='07': continue
            day = get_day(info[Lp[1]:Lp[1] + 5], '07-01')  # 第几天，从'07-01'算起
            slide = get_slide('06:00-' + info[Lp[3]:Lp[3] + 5])  # 第几个时间片，从'06:00'算起
            cur[day, slide] = float(info[Lp[9]:])
        elif int(info[Lp[3]:Lp[3]+2])<5 or int(info[Lp[3]:Lp[3]+2])>=10:
            # 不使用3-6月05:00 到 10:00以外的数据
            continue
        else:
            day=get_day(info[Lp[1]:Lp[1]+5],'03-01') #第几天，从'03-01'算起
            slide=get_slide('05:00-'+info[Lp[3]:Lp[3]+5]) #第几个时间片，从'05:00'算起
            his[day,slide]=float(info[Lp[9]:])
    return his,cur

def construct_train(data,work_day):
    len_w=np.sum(work_day)
    len_f=len(data)-len_w
    wtrainX,wtrainY,ftrainX,ftrainY = np.empty([len_w , 60, 30]),np.empty([len_w , 60, 30]),np.empty([len_f , 60, 30]),np.empty([len_f , 60, 30])
    dw,df=0,0
    for d in range(len(data)):
        if work_day[d]==1: #工作日
            for slide in range(60):
                wtrainX[dw , slide] = np.mean(data[d, slide:slide + 60].reshape([-1,2]),axis=1) #每相邻的两个时间片求平均
                wtrainY[dw , slide] = data[d, slide + 60:slide + 90]
            dw+=1
        else: #节假日
            for slide in range(60):
                ftrainX[df , slide] = np.mean(data[d, slide:slide + 60].reshape([-1,2]),axis=1) #每相邻的两个时间片求平均
                ftrainY[df , slide] = data[d, slide + 60:slide + 90]
            df+=1
    return wtrainX,wtrainY,ftrainX,ftrainY

def construct_val(data):
    valX = np.empty([len(data), 60, 30])
    valY = np.empty([len(data), 60, 30])
    testX = np.empty([len(data), 30])
    testY = np.empty([len(data), 30])
    for d in range(len(data)):
        for slide in range(60):
            valX[d, slide] = np.mean(data[d, slide:slide + 60].reshape([-1,2]),axis=1) #每相邻的两个时间片求平均
            valY[d, slide] = data[d, slide + 60:slide + 90]
        slide = 30  # his从5点开始，第30个时间片是6点
        testX[d] = np.mean(data[d, slide:slide + 60].reshape([-1,2]),axis=1) #每相邻的两个时间片求平均
        testY[d] = data[d, slide + 60:slide + 90]
    return valX, valY,testX,testY

def construct_PreX(data):
    dataX = np.empty([len(data),30])
    for d in range(len(data)): dataX[d] = np.mean(data[d].reshape([-1,2]),axis=1) #每相邻的两个时间片求平均
    return dataX

def construct_sample_set(his,cur,work_day):
    # work_day=(153) 判断从3月1号到7月31号，每天是工作日还是节假日，工作日=1，节假日=0，返回一个153维的numpy数组
    # his=(122,150) 一共是122天，5个小时是5*30=150个时间片，1条Link
    # cur=(31,60) 一共是31天，60个时间片，1条Link
    # 训练集：  针对工作日和节假日分别构造训练集，训练两套模型
    #   工作日：  0301-0616  5:00-10:00  每天有60个样本  一共是75天  wtrainX[75,60,30]  wtrainY[75,60,30]
    #   节假日：  0301-0616  5:00-10:00  每天有60个样本  一共是33天  ftrainX[33,60,30]  ftrainY[33,60,30]
    # 验证集：  0617-0630  5:00-10:00  每天有60个样本  一共是14天  valX[14,60,30]  valY[14,60,30]
    # 验证子集：0617-0630  6:00-9:00  每天有1个样本  一共是14天  testX[14,30]  testY[14,30]
    # 测试集：  0701-0731  6:00-8:00  每天有1个样本  一共是31天  PredX[31,30]
    wtrainX,wtrainY,ftrainX,ftrainY=construct_train(his[:108],work_day[:108]) #his[:108] [108,150]
    valX,valY,testX,testY=construct_val(his[-14:])     # his[-14:]   [14,150]
    PredX=construct_PreX(cur)
    return wtrainX,wtrainY,ftrainX,ftrainY,valX,valY,testX,testY,PredX

def construct_nn_graph1(Train,x,y_,keep_prob,input_node,output_node):#构建神经网络1
    learning_rate = 0.0005
    if Train==0:
        regularizer=tf.contrib.layers.l2_regularizer(0.0005)
        y = ffnn1.inference(x, keep_prob, input_node=input_node, output_node=output_node,regularizer=regularizer)
        cost = tf.reduce_mean(tf.div(tf.abs(y-y_),y_)) + tf.add_n(tf.get_collection('losses'))
    else:
        y=ffnn1.inference(x,keep_prob, input_node=input_node, output_node=output_node)
        cost = tf.reduce_mean(tf.div(tf.abs(y - y_), y_))
    train_op = tf.train.AdamOptimizer(learning_rate).minimize(cost)
    return train_op,cost,y

def construct_nn_graph2(Train,x,y_,keep_prob,input_node,output_node):#构建神经网络2
    learning_rate = 0.0005
    if Train==0:
        regularizer=tf.contrib.layers.l2_regularizer(0.0005)
        y = ffnn2.inference(x, keep_prob, input_node=input_node, output_node=output_node,regularizer=regularizer)
        cost = tf.reduce_mean(tf.div(tf.abs(y-y_),y_)) + tf.add_n(tf.get_collection('losses'))
    else:
        y=ffnn2.inference(x,keep_prob, input_node=input_node, output_node=output_node)
        cost = tf.reduce_mean(tf.div(tf.abs(y - y_), y_))
    train_op = tf.train.AdamOptimizer(learning_rate).minimize(cost)
    return train_op,cost,y

def find_a_best_number(data):#从一个一维数组中找到一个数，使得评价函数最小
    data=np.sort(data)
    data_len=len(data)
    index=int(data_len/2)
    num=data[index]
    score=1e8
    score2=np.mean(np.abs(data-num)/data)
    while score2<=score and index>0:
        index-=1
        score=score2
        score2=np.mean(np.abs(data-data[index])/data)

    if score2>score:
        return data[index+1]
    else:
        return data[index]


from argparse import ArgumentParser
import socket
import ssl
import threading
import logging
import binascii

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def send_to_dns_server(msg, dns, dns_port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(80)
    # Using SSL to have a secure connection to DNS Server
    context = ssl.SSLContext(ssl.PROTOCOL_TLS)
    context.verify_mode = ssl.CERT_REQUIRED
    context.check_hostname = True
    context.load_verify_locations('/etc/ssl/certs/ca-certificates.crt')

    wrapped_socket = context.wrap_socket(sock, server_hostname=dns)
    wrapped_socket.connect((dns, dns_port))

    logger.info("Server peer certificate: %s", str(wrapped_socket.getpeercert()))

    dns_query = "\x00".encode() + chr(len(msg)).encode() + msg

    logger.info("Dns query is %s", str(dns_query))

    wrapped_socket.send(dns_query)

    res = wrapped_socket.recv(1024)
    return res

def process_request(msg, conn, dns, dns_port):
    res = send_to_dns_server(msg, dns, dns_port)
    if res:
        logger.info("Reply: %s", str(res))
        # Get rid of the length part at the begining
        rcode = binascii.hexlify(res[:6]).decode("utf-8")
        # Get the type from request and check if it is correct
        rcode = rcode[11:]
        if int(rcode, 16) == 1:
            logger.error("Error processing the request, RCODE = %s", rcode)
        else:
            logger.info("Proxy OK, RCODE = %s", rcode)
            # Return the answer without size part
            return_ans = res[2:]
            conn.send(return_ans)
    else:
        logger.warning("No DNS Record found or DNS Request is broken")

def main():
    parser = ArgumentParser(description="DNS-over-TLS proxy")
    parser.add_argument(
        "-p",
        "--port",
        help="Listening port (default is 53)",
        type=int,
        default=53,
        required=False,
    )
    parser.add_argument(
        "-a",
        "--address",
        help="Address (default is 0.0.0.0)",
        type=str,
        default="0.0.0.0",
        required=False,
    )
    parser.add_argument(
        "-d",
        "--dns",
        help="Address of Domain Name Server (default is 1.1.1.1 (CloudFlare))",
        type=str,
        default="1.1.1.1",
        required=False,
    )

    parser.add_argument(
        "-dp",
        "--dns-port",
        help="TLS Port of Domain Name Server (default is 853)",
        type=str,
        default="853",
        required=False,
    )

    args = parser.parse_args()
    port = args.port
    host = args.address
    dns = args.dns
    dns_port = args.dns_port

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    logger.info('socket created')
    s.bind((host, port))
    while True:
        s.listen()
        logger.info('Waiting for a connection to accept')
        conn, addr = s.accept()
        with conn:
            logger.info('Yay, a new connection...')
            received_msg = conn.recv(1024)
            logger.info('Connected by %s', addr)
            logger.info('Got data %s', received_msg)

            # Added thread mechanism to handle more than one dns queries at the same time
            threading.Thread(target=process_request, args=(received_msg, conn, dns, dns_port)).start()

if __name__ == '__main__':
    main()