Search code examples
djangodjango-rest-frameworkmql4

MQL4 WebRequest POST Json to Django API using Django rest framework, getting \x00 at the end of body


I am using Django only for few weeks so there might be some mistakes.

I have an API using Django rest framework, which seems to work well. Using postman every thing is ok

I send data using WebRequest in Mql4 to the api

string data = "{ ... }“; 
char post_data[];
post_data = StringToCharArray(data, post_data, WHOLE_ARRAY);
int r = WebRequest("POST", "http://127.0.0.1/api/symbol/","Content-Type: application/json",5000, data,res_data, res_headers);

The data given to WebRequest is fine but the function add \x00 at the end of body.

Here is my post data when it arrive to Django rest framework

{'Content-Length': '982', 'Content-Type': 'application/json', 'Accept': 'image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*', 'Host': '127.0.0.1', 'Accept-Language': 'fr,en', 'Accept-Charset': '*,utf-8', 'Connection': 'Keep-Alive', 'Proxy-Connection': 'Keep-Alive', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'User-Agent': 'MetaTrader 4 Terminal/4.1353 (Windows NT 6.1; x86)'}
b'{"name": "GOLDs","date_market": "2022-02-01T00:00:00.000000Z","period": "M1","open_price": 1797.64,"close_price": 1870.91,"low_price": 1788.33,"high_price": 1879.12,"bollinger_band":[{"period":"M1","moving_average_period":20,"standard_deviation":1.5,"high":1936.06829,"low":1717.61271,"moving_average":1826.8405},{"period":"M1","moving_average_period":20,"standard_deviation":2,"high":1972.47755,"low":1681.20345,"moving_average":1826.8405},{"period":"M1","moving_average_period":20,"standard_deviation":2.5,"high":2008.88681,"low":1644.79419,"moving_average":1826.8405}],"moving_average":[{"period":"M1","moving_average_period":50,"type": "0","value":1569.6854},{"period":"M1","moving_average_period":100,"type": "0","value":1399.8002},{"period":"M1","moving_average_period":200,"type": "0","value":1245.38985}],"MACD_zerolag":[{"period":"M1","fast_EMA_period":12,"slow_EMA_period":26,"signal_EMA_period":9,"histogram_value":-0.01794,"signal_value":0.09465,"MACD_value":0.07671}]}\x00'

so I get the following error :

POST /api/symbol/ - 400
{'Content-Type': 'application/json', 'Vary': 'Accept', 'Allow': 'GET, POST, HEAD, OPTIONS'}
b'{"detail":"JSON parse error - Extra data: line 1 column 982 (char 981)"}'

if a send the same data removing the \x00 with Postman every thing goes well.

Is there a misconfiguration client side with mql4 ?

Or can I remove the received data to remove this \x00 ?

Here is my models.py

    from django.db import models, transaction

M1 = 'M1'
M5 = 'M5'
M15 = 'M15'
M30 = 'M30'
H1 = 'H1'
H4 = 'H4'
DAILY = 'DAILY'
WEEKLY = 'WEEKLY'
MONTHLY = 'MONTHLY'

PERIOD_CHOICES = (
    (M1, 'Une minute'),
    (M5, 'Cinq minutes'),
    (M15, 'Quinze minutes'),
    (M30, 'Trente minutes'),
    (H1, 'Une heure'),
    (H4, 'Quatre heures'),
    (DAILY, 'Journalier'),
    (WEEKLY, 'Hebdomadaire'),
    (MONTHLY, 'Mensuel'),
)


class BollingerBand(models.Model):

    period = models.CharField(max_length=30, choices=PERIOD_CHOICES, verbose_name='Période')
    moving_average_period = models.CharField(max_length=8)
    standard_deviation = models.DecimalField(max_digits=6, decimal_places=2)

    symbol = models.ForeignKey('API.Symbol', on_delete=models.CASCADE, related_name='bollinger_band', default=1)

    high = models.DecimalField(max_digits=12, decimal_places=6)
    low = models.DecimalField(max_digits=12, decimal_places=6)
    moving_average = models.DecimalField(max_digits=12, decimal_places=6)



class MovingAverage(models.Model):

    period = models.CharField(max_length=30, choices=PERIOD_CHOICES, verbose_name='Période')
    moving_average_period = models.CharField(max_length=8)
    type = models.TextField(max_length=30)

    symbol = models.ForeignKey('API.Symbol', on_delete=models.CASCADE, related_name='moving_average', default=1)

    value = models.DecimalField(max_digits=12, decimal_places=6)




class MACDZR(models.Model):

    period = models.CharField(max_length=30, choices=PERIOD_CHOICES, verbose_name='Période')
    fast_EMA_period = models.CharField(max_length=4)
    slow_EMA_period = models.CharField(max_length=4)
    signal_EMA_period = models.CharField(max_length=4)

    symbol = models.ForeignKey('API.Symbol', on_delete=models.CASCADE, related_name='MACD_zerolag', default=1)

    histogram_value = models.DecimalField(max_digits=12, decimal_places=6)
    signal_value = models.DecimalField(max_digits=12, decimal_places=6)
    MACD_value = models.DecimalField(max_digits=12, decimal_places=6)





class Symbol(models.Model):


    name = models.CharField(max_length=30, default='undefined')
    date_created = models.DateTimeField(auto_now_add=True)
    date_updated = models.DateTimeField(auto_now=True)

    date_market = models.DateTimeField()

    period = models.CharField(max_length=30, choices=PERIOD_CHOICES, verbose_name='Période')

    open_price = models.DecimalField(max_digits=12, decimal_places=6)
    close_price = models.DecimalField(max_digits=12, decimal_places=6)
    high_price = models.DecimalField(max_digits=12, decimal_places=6)
    low_price = models.DecimalField(max_digits=12, decimal_places=6)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['name', 'date_market', 'period'],
                name='unique_symbol'
            )
        ]

    def __str__(self):
        return self.name

Serializers.py

from rest_framework import serializers

from API.models import Symbol, BollingerBand, MACDZR, MovingAverage


class BollingerBandSerializer(serializers.ModelSerializer):

    class Meta:
        model = BollingerBand
        fields = ['period',
                  'moving_average_period',
                  'standard_deviation',
                  'symbol',
                  'high',
                  'low',
                  'moving_average']



class MovingAverageSerializer(serializers.ModelSerializer):

    class Meta:
        model = MovingAverage
        fields = ['period',
                  'moving_average_period',
                  'type',
                  'symbol',
                  'value']



class MACDZRSerializer(serializers.ModelSerializer):

    class Meta:
        model = MACDZR
        fields = ['period',
                  'fast_EMA_period',
                  'slow_EMA_period',
                  'signal_EMA_period',
                  'symbol',
                  'histogram_value',
                  'signal_value',
                  'MACD_value']



class SymbolSerializer(serializers.ModelSerializer):

    bollinger_band = BollingerBandSerializer(many=True)
    moving_average = MovingAverageSerializer(many=True)
    MACD_zerolag = MACDZRSerializer(many=True)

    class Meta:
        model = Symbol
        fields = ['id',
                  'name',
                  'date_created',
                  'date_updated',
                  'date_market',
                  'period',
                  'open_price',
                  'close_price',
                  'high_price',
                  'low_price',
                  'bollinger_band',
                  'moving_average',
                  'MACD_zerolag'
                  ]


    def create(self, validated_data):

        bollinger_bands_data = validated_data.pop('bollinger_band')
        moving_averages_data = validated_data.pop('moving_average')
        MACDs_zerolag_data = validated_data.pop('MACD_zerolag')

        symbol = Symbol.objects.create(**validated_data)

        for bollinger_band_data in bollinger_bands_data:
            BollingerBand.objects.create(symbol=symbol, **bollinger_band_data)

        for MACD_zerolag_data in MACDs_zerolag_data:
            MACDZR.objects.create(symbol=symbol, **MACD_zerolag_data)

        for moving_average_data in moving_averages_data:
            MovingAverage.objects.create(symbol=symbol, **moving_average_data)

        return symbol

and settings.py

"""
Django settings for trading_project project.

Generated by 'django-admin startproject' using Django 4.0.2.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/
"""

from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-)6f&ci7@)t96(k^7am%66@%e%(88a%u00+3h5a13s461#q7dqm'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = [
    '0.0.0.0',
    '127.0.0.1',
]


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    #'django.contrib.sites',
    'rest_framework',
    'authentication',
    'API',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'request_logging.middleware.LoggingMiddleware',
]

ROOT_URLCONF = 'trading_project.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates/'),
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'trading_project.wsgi.application'


# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'trading_project',
        'USER': 'admin',
        'PASSWORD': 'testpassword',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}


# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/

LANGUAGE_CODE = 'fr-fr'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/

STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

AUTH_USER_MODEL = 'authentication.User'
LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'home'
LOGOUT_URL = 'logout'
LOGOUT_REDIRECT_URL = 'login'


LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.request': {
            'handlers': ['console'],
            'level': 'DEBUG',  # change debug level as appropiate
            'propagate': False,
        },
    },
}

if anyone have a simple solution for that Thanks


Solution

  • I find my solution, when I converted my data string to post_Data object the length was not WHOLE_ARRAY, but StringLen.

    StringToCharArray(data, post_data,0, StringLen(data));