class Primitive(object):
class Specifier(object):
class DateTime(Primitive):
class AccountFinancingMode(str, Primitive):
class AccountID(str, Primitive):
class PositionAggregationMode(str, Primitive):
class CandlestickGranularity(str, Primitive):
class WeeklyAlignment(str, Primitive):
class PriceComponent(str, Primitive):
class CancellableOrderType(str, Primitive):
class OrderID(int, Primitive, Specifier):
class OrderPositionFill(str, Primitive):
class OrderSpecifier(str, Primitive, Specifier):
class OrderState(str, Primitive):
class OrderStateFilter(str, Primitive):
class OrderTriggerCondition(str, Primitive):
class OrderType(str, Primitive):
class TimeInForce(str, Primitive):
class PriceStatus(str, Primitive):
class PriceValue(float, Primitive):
class AcceptDatetimeFormat(str, Primitive):
class AccountUnits(float, Primitive):
class Currency(str, Primitive):
class DecimalNumber(float, Primitive):
class Direction(str, Primitive):
class InstrumentName(str, Primitive):
class InstrumentType(str, Primitive):
class TradeSpecifier(str, Primitive, Specifier):
class TradeID(int, Primitive, Specifier):
class TradePL(str, Primitive):
class TradeState(str, Primitive):
class TradeStateFilter(str, Primitive):
class ClientComment(str, Primitive):
class ClientID(str, Primitive, Specifier):
class ClientTag(str, Primitive):
class Reason(Primitive, str):
class FundingReason(Reason):
class LimitOrderReason(Reason):
class MarketIfTouchedOrderReason(Reason):
class MarketOrderMarginCloseoutReason(Reason):
class MarketOrderReason(Reason):
class OrderCancelReason(Reason):
class OrderFillReason(Reason):
class RequestID(str, Primitive):
class StopLossOrderReason(Reason):
class StopOrderReason(Reason):
class TakeProfitOrderReason(Reason):
class TrailingStopLossOrderReason(Reason):
class TransactionFilter(str, Primitive):
class TransactionID(int, Primitive, Specifier):
class TransactionRejectReason(Reason):
class TransactionType(str, Primitive):
class GuaranteedStopLossOrderMode(str, Primitive):
    def __new__(cls, value, **kwargs):
def _datetime_to_json(self, datetime_format):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def format(self, precision, min_=None, max_=None):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def format(self, precision, min_=None, max_=None):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __init_subclass__(cls, **kwargs):
    def __new__(cls, value, possible_values=None):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value):
    def __new__(cls, value): class ClientComment(str, Primitive):
    """A client-provided comment that can contain any data and may be assigned to their Orders or
    Trades. Comments are typically used to provide extra context or meaning to an Order or Trade.
    """

    # Example of correct format
    example = 'This is a client comment'

    def __new__(cls, value):
        return super().__new__(cls, value) class ClientID(str, Primitive, Specifier):
    """A client-provided identifier, used by clients to refer to their
    Orders or Trades with an identifier that they have provided.
    """

    # Example of correct format
    example = 'my_order_id'

    def __new__(cls, value):
        return super().__new__(cls, value) class ClientTag(str, Primitive):
    """A client-provided tag that can contain any data and may be assigned to their
    Orders or Trades. Tags are typically used to associate groups of Trades and/or Orders together.
    """

    # Example of correct format
    example = 'client_tag_1'

    def __new__(cls, value):
        return super().__new__(cls, value) class OrderID(int, Primitive, Specifier):
    """The Order’s identifier, unique within the Order’s Account.
    """

    # Correct syntax of value
    format_syntax = 'The string representation of the OANDA-assigned OrderID. ' \
                    'OANDA-assigned OrderIDs are positive integers, and are derived from the ' \
                    'TransactionID of the Transaction that created the Order.'

    # Example of correct format
    example = '1523'

    def __new__(cls, value):
        return super().__new__(cls, value) class TradeID(int, Primitive, Specifier):
    """The Trade’s identifier, unique within the Trade’s Account.
    """

    # Correct syntax of value
    format_syntax = 'The string representation of the OANDA-assigned TradeID. ' \
                    'OANDA-assigned https://github.com/jamespeterschinner/async_v20/issues/12s are positive integers, and are ' \
                    'derived from the TransactionID of the Transaction that opened the Trade.'
    # Example of correct format
    example = '1523'

    def __new__(cls, value):
        return super().__new__(cls, value) class OrderSpecifier(str, Primitive, Specifier):
    """The specification of an Order as referred to by clients
    """

    # Correct syntax of value
    format_syntax = 'Either the Order’s OANDA-assigned OrderID or the Order’s client-provided ' \
                    'ClientID prefixed by the "@" symbol'
    # Example of correct format
    example = '1523'

    def __new__(cls, value):
        return super().__new__(cls, value) class TradeSpecifier(str, Primitive, Specifier):
    """The identification of a Trade as referred to by clients
    """

    # Correct syntax of value
    format_syntax = 'Either the Trade’s OANDA-assigned TradeID or the Trade’s client-provided ' \
                    'ClientID prefixed by the "@" symbol'
    # Example of correct format
    example = '@my_trade_id'

    def __new__(cls, value):
        return super().__new__(cls, value) class DateTime(Primitive):
    """A date and time value using either RFC3339 or UNIX time representation.
    """

    def __new__(cls, value, **kwargs):
        if not isinstance(value, (int, float, str)):
            pass
        else:
            value = str(value)
            if 'Z' not in value:
                seconds, decimal, fraction = value.partition('.')

                if not fraction and len(seconds) > 10:
                    seconds, fraction = value[:10], value[10:]
                # value has decimal number
                if fraction:
                    fraction = fraction + '000000000'[:-len(fraction)]
                else:
                    fraction = '000000000'

                value = int(seconds + fraction)
                kwargs.update(tz='UTC')

        return pd.Timestamp(value, **kwargs) class TransactionID(int, Primitive, Specifier):
    """The unique Transaction identifier within each Account.
    """

    # Correct syntax of value
    format_syntax = 'String representation of the numerical OANDA-assigned TransactionID'
    # Example of correct format
    example = '1523'

    def __new__(cls, value):
        return super().__new__(cls, value) class PriceValue(float, Primitive):
    """The string representation of a Price for an Instrument.
    """

    # Correct syntax of value
    format_syntax = 'A decimal number encodes as a string. The amount of precision ' \
                    'provided depends on the Price’s Instrument. Must be positive'

    def __new__(cls, value):
        try:
            assert float(value) >= 0
        except AssertionError:
            msg = f'Cannot create PriceValue from {value}. PriceValues must be positive'
            logger.error(msg)
            raise InvalidValue(msg)
        return super().__new__(cls, value)

    def format(self, precision, min_=None, max_=None):
        # Ignore the sign. `self` should be positive
        if not precision >= 0:
            msg = f'Cannot format PriceValue. precision {precision} must be >= 0'
            logger.error(msg)
            raise InvalidFormatArguments(msg)
        if min_ and max_:
            if not min_ <= max_:
                msg = f'Cannot format PriceValue. min_ {min_} is not <= max_ {max_}'
                logger.error(msg)
                raise InvalidFormatArguments(msg)

        value = self
        if min_:
            value = (value, min_)[value < min_]
        if max_:
            value = (value, max_)[value > max_]

        return super().__new__(self.__class__, round(value, precision)) class DecimalNumber(float, Primitive):
    """The string representation of a decimal number.
    """

    # Correct syntax of value
    format_syntax = 'A decimal number encoded as a string. The amount of precision ' \
                    'provided depends on what the number represents.'

    def __new__(cls, value):
        return super().__new__(cls, value)

    def format(self, precision, min_=None, max_=None):
        # A DecimalNumber can se + / -
        if not precision >= 0:
            msg = f'Cannot format PriceValue. precision {precision} must be >= 0'
            logger.error(msg)
            raise InvalidFormatArguments(msg)

        if min_ and max_:
            if not min_ <= max_:
                msg = f'Cannot format PriceValue. min_ {min_} is not <= max_ {max_}'
                logger.error(msg)
                raise InvalidFormatArguments(msg)

        value = abs(self)
        if min_:
            value = (value, min_)[value < min_]
        if max_:
            value = (value, max_)[value > max_]
        value = value * (1, -1)[self < 0]

        return super().__new__(self.__class__, round(value, precision)) def get_valid_primitive_data(primitive):
    data = None
    if primitive == _empty:
        return
    elif issubclass(primitive, DateTime): #or primitive == FromTime or primitive == ToTime:
        # Means it is a time
        return '2017-12-19T21:27:45.000000000Z'
    elif issubclass(primitive, Array):
        return (get_valid_primitive_data(primitive._contains),)
    elif issubclass(primitive, Model):
        return {attr: get_valid_primitive_data(create_cls_annotations(primitive)[attr])
                for attr in primitive.__annotations__}
    elif issubclass(primitive, InstrumentName):
        return 'AUD_USD'
    if issubclass(primitive, (float)):
        data = 14.0
    elif issubclass(primitive, (int)):
        try:
            data = primitive()  # See if the annotation has a default value
        except TypeError:
            data = 123456789
    elif issubclass(primitive, (Bool, dict)):
        # dict covers ArrayDict used for `tags` attribute of Instrument class
        data = primitive()
    # The only valid option here should be a subclass of str
    else:

        assert issubclass(primitive, (str))

        try:
            data = primitive.example
        except AttributeError:
            try:
                data = next(iter(primitive.values))
            except AttributeError:
                data = '1'

    return data class InvalidValue(AsyncV20Exception):
    """A supplied value does not meet the specification
    of valid values"""
    pass class InvalidFormatArguments(AsyncV20Exception):
    """Arguments to format a DecimalNumber or PriceValue
    are invalid"""
    pass
from inspect import isclass
from async_v20.definitions import primitives
from async_v20.definitions.primitives import ClientComment, ClientID, ClientTag, OrderID, TradeID
from async_v20.definitions.primitives import OrderSpecifier, TradeSpecifier, DateTime
from async_v20.definitions.primitives import TransactionID, PriceValue, DecimalNumber
from tests.test_definitions.helpers import get_valid_primitive_data
from async_v20.exceptions import InvalidValue, InvalidFormatArguments
from time import time
from datetime import datetime
import pytest
import logging
import pandas as pd


logger = logging.getLogger('async_v20')
logger.disabled = True


def test_date_time_can_be_created_from_time_and_datetime():
    assert DateTime(time())
    assert DateTime(datetime.now())

def test_date_time_can_be_created_from_integer():
    t = time()
    assert DateTime(t).second == DateTime(int(t)).second
    assert DateTime(t).second == DateTime(str(t).replace('.', '')).second

def test_datetime_converts_between_different_representations():
    unix_example = '1502463871.639182000'
    rfc3339_example = '2017-08-11T15:04:31.639182000Z'
    assert DateTime(unix_example) == DateTime(rfc3339_example)
    assert DateTime(rfc3339_example).json('UNIX') == unix_example
    assert DateTime(DateTime(rfc3339_example).json('UNIX')) == DateTime(rfc3339_example)
    assert DateTime(unix_example).json('RFC3339') == rfc3339_example


def test_datetime_format_only_allows_valid_format_string():
    unix_example = '1502463871.639182000'
    with pytest.raises(InvalidValue):
        assert DateTime(unix_example).json(datetime_format='BADVALUE')

@pytest.mark.parametrize('primitive', map(lambda x: getattr(primitives, x), primitives.__all__))
def test_get_valid_primitive_data(primitive):
    """Test the helper function can provide valid data for all primitives"""
    assert get_valid_primitive_data(primitive)


# Get All user defined class' in primitive package
@pytest.mark.parametrize('primitive', map(lambda x: getattr(primitives, x), primitives.__all__))
def test_all_incorrect_primitive_values_cannot_be_assigned(primitive):
    if isclass(primitive):
        if getattr(primitive, 'values', None):
            for key in primitive.values.keys():
                with pytest.raises(InvalidValue):
                    primitive(key + '_test')


# Get All user defined class' in primitive package
@pytest.mark.parametrize('primitive', map(lambda x: getattr(primitives, x), primitives.__all__))
def test_primitive_values_have_length_checking(primitive):
    if isclass(primitive):
        if getattr(primitive, 'values', None):
            for key in primitive.values.keys():
                with pytest.raises(InvalidValue):
                    primitive(key + '_test')


# Get All user defined class' in primitive package
@pytest.mark.parametrize('primitive', map(lambda x: getattr(primitives, x), primitives.__all__))
def test_primitives_enforce_length_checking(primitive):
    if isclass(primitive):
        if getattr(primitive, 'example', None) and primitive not in \

                (OrderSpecifier, ClientComment, ClientID, ClientTag, TransactionID, TradeSpecifier,