English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Italiano
Sosyal Teknoloji Girişimi Kurma, Bölüm II: Bir MQL5 REST İstemcisi Programlama

Sosyal Teknoloji Girişimi Kurma, Bölüm II: Bir MQL5 REST İstemcisi Programlama

MetaTrader 5Ticaret | 14 Ocak 2022, 12:48
198 0
laplacianlab
[Silindi]

Giriş

Bu makalenin önceki bölümünde, Sosyal Karar Destek Sistemi adı verilen bir sistemin mimarisini sunduk. Bu sistem bir yandan Expert Advisor'ların otomatik kararlarını sunucu tarafına gönderen bir MetaTrader 5 terminalinden oluşmaktadır. İletişimin diğer tarafında, bu alım satım sinyallerini alan, bunları bir MySQL veritabanında saklayan ve sonunda kişilere tweet atan Slim PHP çerçevesi üzerine kurulu bir Twitter uygulaması var. SDSS'nin temel amacı, robotik sinyaller üzerinde gerçekleştirilen insan eylemlerini kaydetmek ve buna göre insan kararları vermektir. Bu mümkündür, çünkü robotik sinyaller bu şekilde çok geniş bir uzman kitlesi için sunulabilir.

Buradaki ikinci bölümde SDSS'nin istemci tarafını MQL5 programlama dili ile geliştireceğiz. Her birinin artılarını ve eksilerini belirlemenin yanı sıra bazı alternatifleri tartışıyoruz. Daha sonra, bulmacanın tüm parçalarını bir araya getireceğiz ve Expert Advisor'dan alım satım sinyalleri alan PHP REST API'sini şekillendireceğiz. Bunu başarmak için, istemci tarafı programlamasında yer alan bazı yönleri dikkate almalıyız.

Artık MQL5 alım satım sinyallerinizi tweetleyebilirsiniz!

Artık MQL5 alım satım sinyallerinizi tweetleyebilirsiniz!


1. SDSS'nin İstemci Tarafı

1.1. OnTimer Olayındaki Bazı İşlem Sinyallerini Tweetleme

Sadelikle ilgili sorunlar için alım satım sinyallerinin OnTimer olayından nasıl gönderildiğini göstermeyi düşündüm. Bu basit örneğin nasıl çalıştığını gördükten sonra, standart bir Expert Advisor için bunun gibi temel bir hareketi tahmin etmek çok kolay olacaktır.

dummy_ontimer.mq5:

#property copyright     "Author: laplacianlab, CC Attribution-Noncommercial-No Derivate 3.0"
#property link          "https://www.mql5.com/en/users/laplacianlab"
#property version       "1.00"
#property description   "Simple REST client built on the OnTimer event for learning purposes."

int OnInit()
  {          
   EventSetTimer(10);    
   return(0);  
  }
  
void OnDeinit(const int reason)
  {  
  }
  
void OnTimer()
  {
//--- REST client's HTTP vars
   string uri="http://api.laplacianlab.com/signal/add";
   char post[];
   char result[];
   string headers;
   int res;
   string signal = "id_ea=1&symbol=AUDUSD&operation=BUY&value=0.9281&";
   StringToCharArray(signal,post);
//--- reset last error
   ResetLastError();
//--- post data to REST API
   res=WebRequest("POST",uri,NULL,NULL,50,post,ArraySize(post),result,headers);
//--- check errors
   if(res==-1)
     {
      Print("Error code =",GetLastError());
      //--- maybe the URL is not added, show message to add it
      MessageBox("Add address '"+uri+"' in Expert Advisors tab of the Options window","Error",MB_ICONINFORMATION);
     }
   else
     {
      //--- successful
      Print("REST client's POST: ",signal);
      Print("Server response: ",CharArrayToString(result,0,-1));
     }         
  }

Gördüğünüz gibi, bu istemci uygulamasının merkezi kısmı yeni MQL5'in WebRequest işlevidir.

HTTP iletişimi ile yatırım yapmak için özel bir MQL5 bileşeni programlamak bu çözüm için bir alternatif olabilir, ancak bu görevi ile bu yeni dil özelliğini MetaQuotes'a devretmek daha güvenlidir.

Yukarıdaki MQL5 programı aşağıdaki çıktıları verir:

OR      0       15:43:45.363    RESTClient (EURUSD,H1)  REST client's POST: id_ea=1&symbol=AUDUSD&operation=BUY&value=0.9281&
KK      0       15:43:45.365    RESTClient (EURUSD,H1)  Server response: {"id_ea":"1","symbol":"AUDUSD","operation":"BUY","value":"0.9281","id":77}
PD      0       15:43:54.579    RESTClient (EURUSD,H1)  REST client's POST: id_ea=1&symbol=AUDUSD&operation=BUY&value=0.9281&
CE      0       15:43:54.579    RESTClient (EURUSD,H1)  Server response: {"status": "ok", "message": {"text": "Please wait until the time window has elapsed."}}
ME      0       15:44:04.172    RESTClient (EURUSD,H1)  REST client's POST: id_ea=1&symbol=AUDUSD&operation=BUY&value=0.9281&
JD      0       15:44:04.172    RESTClient (EURUSD,H1)  Server response: {"status": "ok", "message": {"text": "Please wait until the time window has elapsed."}}
NE      0       15:44:14.129    RESTClient (EURUSD,H1)  REST client's POST: id_ea=1&symbol=AUDUSD&operation=BUY&value=0.9281&
ID      0       15:44:14.129    RESTClient (EURUSD,H1)  Server response: {"status": "ok", "message": {"text": "Please wait until the time window has elapsed."}}
NR      0       15:44:24.175    RESTClient (EURUSD,H1)  REST client's POST: id_ea=1&symbol=AUDUSD&operation=BUY&value=0.9281&
IG      0       15:44:24.175    RESTClient (EURUSD,H1)  Server response: {"status": "ok", "message": {"text": "Please wait until the time window has elapsed."}}
MR      0       15:44:34.162    RESTClient (EURUSD,H1)  REST client's POST: id_ea=1&symbol=AUDUSD&operation=BUY&value=0.9281&
JG      0       15:44:34.162    RESTClient (EURUSD,H1)  Server response: {"status": "ok", "message": {"text": "Please wait until the time window has elapsed."}}
PR      0       15:44:44.179    RESTClient (EURUSD,H1)  REST client's POST: id_ea=1&symbol=AUDUSD&operation=BUY&value=0.9281&
CG      0       15:44:44.179    RESTClient (EURUSD,H1)  Server response: {"status": "ok", "message": {"text": "Please wait until the time window has elapsed."}}
HS      0       15:44:54.787    RESTClient (EURUSD,H1)  REST client's POST: id_ea=1&symbol=AUDUSD&operation=BUY&value=0.9281&
KJ      0       15:44:54.787    RESTClient (EURUSD,H1)  Server response: {"id_ea":"1","symbol":"AUDUSD","operation":"BUY","value":"0.9281","id":78}
DE      0       15:45:04.163    RESTClient (EURUSD,H1)  REST client's POST: id_ea=1&symbol=AUDUSD&operation=BUY&value=0.9281&
OD      0       15:45:04.163    RESTClient (EURUSD,H1)  Server response: {"status": "ok", "message": {"text": "Please wait until the time window has elapsed."}}

Lütfen sunucunun şu mesajla cevap verdiğini unutmayın:

{"status": "ok", "message": {"text": "Please wait until the time window has elapsed."}}

Bunun nedeni, SDSS'yi hiperaktif scalper robotlarından korumak için API yöntemi sinyal/ekleme yönteminde uygulanan küçük bir güvenlik mekanizmasının olmasıdır:

/**
 * REST method.
 * Adds and tweets a new trading signal.
 */
$app->post('/signal/add', function() {
    $tweeterer = new Tweeterer();
    // This condition is a simple mechanism to prevent hyperactive scalpers
    if ($tweeterer->canTweet($tweeterer->getLastSignal(1)->created_at, '1 minute'))
    {
        $signal = (object)($_POST);
        $signal->id = $tweeterer->addSignal(1, $signal);
        $tokens = $tweeterer->getTokens(1);
        $connection = new TwitterOAuth(
            API_KEY, 
            API_SECRET, 
            $tokens->access_token, 
            $tokens->access_token_secret);
        $connection->host = "https://api.twitter.com/1.1/";
        $ea = new EA();
        $message = "{$ea->get($signal->id_ea)->name} on $signal->symbol. $signal->operation at $signal->value";
        $connection->post('statuses/update', array('status' => $message));           
        echo '{"status": "ok", "message": {"text": "Signal processed."}}';
    }   
});

Yukarıdaki basit mekanizma, web sunucusu gelen HTTP talebinin kötü amaçlı olmadığını kontrol ettikten hemen sonra web uygulamasında devreye girer (örneğin, gelen sinyal herhangi bir hizmet reddi saldırısı değildir).

Web sunucusu bu tür saldırıları önlemekten sorumlu olabilir. Örnek olarak Apache, kaçamak ve güvenlik modüllerini birleştirerek bunları önleyebilir.

Bu sunucu yöneticisinin, uygulamanın saniye başına kabul edebileceği HTTP isteklerini vb. kontrol edebildiği tipik bir Apache mod_evasive yapılandırmasıdır.

<IfModule mod_evasive20.c>
DOSHashTableSize    3097
DOSPageCount        2
DOSSiteCount        50
DOSPageInterval     1
DOSSiteInterval     1
DOSBlockingPeriod   60
DOSEmailNotify someone@somewhere.com
</IfModule>

Yani söylediğimiz gibi, canTweet PHP yönteminin amacı, SDSS tarafından HTTP saldırısı olarak kabul edilmeyen hiperaktif spekülatörleri engellemektir. canTweet yöntemi, Twetterer sınıfında uygulanır (bu daha sonra tartışılacaktır):

/**
 * Checks if it's been long enough so that the tweeterer can tweet again
 * @param string $timeLastTweet e.g. 2014-07-05 15:26:49
 * @param string $timeWindow A time window, e.g. 1 hour
 * @return boolean
 */
public function canTweet($timeLastTweet=null, $timeWindow=null)
{
    if(!isset($timeLastTweet)) return true;
    $diff = time() - strtotime($timeLastTweet);
    switch($timeWindow)
    {
        case '1 minute';                
            $diff <= 60 ? $canTweet = false : $canTweet = true;                
            break;                
        case '1 hour';                
            $diff <= 3600 ? $canTweet = false : $canTweet = true;                
            break;                
        case '1 day':                                
            $diff <= 86400 ? $canTweet = false : $canTweet = true;                
            break;
        default:                
            $canTweet = false;                
            break;                
    } 
    if($canTweet)
    {
        return true;
    }
    else 
    {
        throw new Exception('Please wait until the time window has elapsed.');
    }
}

Şimdi WebRequest'in bizim için otomatik olarak oluşturduğu bazı HTTP talep başlık alanlarını görelim:

Content-Type: application/x-www-form-urlencoded
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*

WebRequest'in POST'u, programcıların bazı HTML form verilerini göndermek istediğini varsayar ama yine de bu senaryoda aşağıdaki HTTP talep başlık alanlarını sunucuya göndermek isteriz:

Content-Type: application/json
Accept: application/json

Sihirli değnek olmadığına göre, kararımızla tutarlı olmalı ve artıları ve eksileri keşfetmek için WebRequest'in gereksinimlerimizi nasıl karşıladığını etraflıca incelemeliyiz.

Teknik açıdan tamamen HTTP REST diyalogları kurmak daha doğru olurdu, ama dediğimiz gibi WebRequest() web hizmetleri için değil, web sayfaları için tasarlanmış gibi görünse de HTTP diyaloglarını MetaQuotes'a devretmek daha güvenli bir çözüm. Bu nedenle, istemcinin alım satım sinyalini kodlayan url'yi sonlandıracağız. API url kodlu sinyalleri alacak ve ardından bunları PHP'nin stdClass formatına dönüştürecektir.

WebRequest() işlevini kullanmanın bir alternatifi, wininet.dll kitaplığını kullanarak işletim sistemine yakın düzeyde çalışan özel bir MQL5 bileşeni yazmaktır. Terminaller arasında Veri Alışverişi için İnternet üzerinden WinInet.dll Kullanımı ve MQL5'te WinInet'i Kullanma makaleleri. Bölüm 2: POST İstekleri ve Dosyaları bu yaklaşımın temellerini açıklar. Ancak MQL5 geliştiricilerinin ve MQL5 Topluluğunun deneyimleri, bu çözümün ilk bakışta göründüğü kadar kolay olmadığını göstermiştir. MetaTrader güncellendiğinde WinINet işlevlerine yapılan çağrıların bozulması gibi bir dezavantaj sunabilir.

1.2. Bir EA'nın İşlem Sinyallerini Tweetleme

Şimdi son zamanlarda açıkladıklarımız hakkında çıkarımda bulunalım. Kontrollü spekülasyon ve hizmet saldırılarını engellemeyle ilgili sorunu göstermek için aşağıda bir kukla robotu oluşturdum.

Dummy.mq5:

//+------------------------------------------------------------------+
//|                                                        Dummy.mq5 |
//|                               Copyright © 2014, Jordi Bassagañas |
//+------------------------------------------------------------------+
#property copyright     "Author: laplacianlab, CC Attribution-Noncommercial-No Derivate 3.0"
#property link          "https://www.mql5.com/en/users/laplacianlab"
#property version       "1.00"
#property description   "Dummy REST client (for learning purposes)."
//+------------------------------------------------------------------+
//| Trade class                                                      |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
//+------------------------------------------------------------------+
//| Declaration of variables                                         |
//+------------------------------------------------------------------+
CPositionInfo PositionInfo;
CTrade trade;
MqlTick tick;
int stopLoss = 20;
int takeProfit = 20;
double size = 0.1;
//+------------------------------------------------------------------+
//| Tweet trading signal                                             |
//+------------------------------------------------------------------+   
void Tweet(string uri, string signal)
  {
   char post[];
   char result[];
   string headers;
   int res;
   StringToCharArray(signal,post);
//--- reset last error
   ResetLastError();
//--- post data to REST API
   res=WebRequest("POST",uri,NULL,NULL,50,post,ArraySize(post),result,headers);
//--- check errors
   if(res==-1)
     {
      //--- error
      Print("Error code =",GetLastError());
     }
   else
     {
      //--- successful
      Print("REST client's POST: ",signal);
      Print("Server response: ",CharArrayToString(result,0,-1));
     }         
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {                
   return(0);  
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+  
void OnDeinit(const int reason)
  {  
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+  
void OnTick()
  {
//--- update tick
   SymbolInfoTick(_Symbol, tick);
//--- calculate Take Profit and Stop Loss levels
   double tp;
   double sl;   
   sl = tick.ask + stopLoss * _Point;
   tp = tick.bid - takeProfit * _Point;
//--- open position
   trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,size,tick.bid,sl,tp);
//--- trade URL-encoded signal "id_ea=1&symbol=AUDUSD&operation=BUY&value=0.9281&";
   string signal = "id_ea=1&symbol=" + _Symbol + "&operation=SELL&value=" + (string)tick.bid + "&";
   Tweet("http://api.laplacianlab.com/signal/add",signal);
}

Yukarıdaki kod daha kolay olamaz. Bu Expert Advisor, her tick'e yalnızca tek bir kısa pozisyon yerleştirir. Bu nedenle robotu özellikle çok fazla volatilitenin olduğu bir zamanda çalıştırırsanız, kısa bir zaman aralığında birçok pozisyon yerleştirmesi çok muhtemeldir. Endişelenmek için hiçbir sebep yok. Açıklandığımız gibi sunucu kısmı, hem web sunucusunu DoS saldırılarını önleyecek şekilde yapılandırarak hem de PHP uygulamasında belirli bir zaman penceresi tanımlayarak tweetleme aralığını kontrol eder.

Tüm bunların netliğiyle, artık EA'nın Tweet işlevini alabilir ve en sevdiğiniz Expert Advisor'a yerleştirebilirsiniz.

1.3. Kullanıcılar tweetlenmiş alım satım sinyallerini nasıl görüyor?

Aşağıdaki örnekte, @laplacianlab önceki bölümde yayınlanan sahte EA sinyallerini tweet atması için SDSS'ye izin verir:

Şekil 1 @laplacianlab onun adına tweet atması için SDSS'ye izin verir

Şekil 1 @laplacianlab onun adına tweet atması için SDSS'ye izin verdi

Bu arada, bu örnekte ad olarak Bollinger Bantları görünüyor çünkü bu, bu makalenin ilk bölümünde MySQL veritabanında sakladığımız addır. id_ea=1 "Bollinger Bantları" ile ilişkilendirildi, ancak bu açıklamaya tam olarak uyması için onu "Kukla" gibi bir şeyle değiştirmeliydik. Her durumda bu ikincil bir husustur, ancak bu küçük rahatsızlık için özür dilerim.

MySQL veritabanı sonunda aşağıdaki gibidir:

# MySQL database creation...

CREATE DATABASE IF NOT EXISTS laplacianlab_com_sdss;

use laplacianlab_com_sdss;

CREATE TABLE IF NOT EXISTS twitterers (
    id mediumint UNSIGNED NOT NULL AUTO_INCREMENT, 
    twitter_id VARCHAR(255),
    access_token TEXT,
    access_token_secret TEXT,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    PRIMARY KEY (id)
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS eas (
    id mediumint UNSIGNED NOT NULL AUTO_INCREMENT, 
    name VARCHAR(32),
    description TEXT,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),    
    PRIMARY KEY (id)
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS signals (
    id int UNSIGNED NOT NULL AUTO_INCREMENT,
    id_ea mediumint UNSIGNED NOT NULL,
    id_twitterer mediumint UNSIGNED NOT NULL,
    symbol VARCHAR(10) NOT NULL,
    operation VARCHAR(6) NOT NULL,
    value DECIMAL(9,5) NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    PRIMARY KEY (id),
    FOREIGN KEY (id_ea) REFERENCES eas(id),
    FOREIGN KEY (id_twitterer) REFERENCES twitterers(id)
) ENGINE=InnoDB;

# Dump some sample data...

# As explained in Part I, there's one single twitterer

INSERT INTO eas(name, description) VALUES
('Bollinger Bands', '<p>Robot based on Bollinger Bands. Works with H4 charts.</p>'),
('Two EMA', '<p>Robot based on the crossing of two MA. Works with H4 charts.</p>');


2. SDSS'nin Sunucu Tarafı

Sosyal Karar Destek Sistemimizin sunucu tarafını şekillendirmeye devam etmeden önce şu anda aşağıdaki dizin yapısına sahip olduğumuzu kısaca hatırlayalım:

Şekil 2. Slim tabanlı PHP API'sinin dizin yapısı

Şekil 2. Slim tabanlı PHP API'sinin dizin yapısı

2.1. PHP API Kodu

Anlatılana göre şimdi index.php dosyası şöyle görünmelidir:

<?php
/**
 * Laplacianlab's SDSS - A REST API for tweeting MQL5 trading signals
 *
 * @author      Jordi Bassagañas
 * @copyright   2014 Jordi Bassagañas
 * @link        https://www.mql5.com/en/users/laplacianlab
 */

/* Bootstrap logic */
require_once 'config/config.php';
set_include_path(get_include_path() . PATH_SEPARATOR . APPLICATION_PATH . '/vendor/');
set_include_path(get_include_path() . PATH_SEPARATOR . APPLICATION_PATH . '/model/');
require_once 'slim/slim/Slim/Slim.php';
require_once 'abraham/twitteroauth/twitteroauth/twitteroauth.php';
require_once 'Tweeterer.php';
require_once 'EA.php';
session_start();

/* Init Slim */
use \Slim\Slim;
Slim::registerAutoloader();
$app = new Slim(array('debug' => false));
$app->response->headers->set('Content-Type', 'application/json');

/**
 * Slim's exception handler
 */
$app->error(function(Exception $e) use ($app) {
    echo '{"status": "error", "message": {"text": "' . $e->getMessage() . '"}}';
});

/**
 * REST method.
 * Custom 404 error.
 */
$app->notFound(function () use ($app) {
    echo '{"status": "error 404", "message": {"text": "Not found."}}';
});

/**
 * REST method.
 * Home page.
 */
$app->get('/', function () {
    echo '{"status": "ok", "message": {"text": "Service available, please check API."}}';
});

/**
 * REST method.
 * Adds and tweets a new trading signal.
 */
$app->post('/signal/add', function() {
    $tweeterer = new Tweeterer();
    // This condition is a simple mechanism to prevent hyperactive scalpers
    if ($tweeterer->canTweet($tweeterer->getLastSignal(1)->created_at, '1 minute'))
    {
        $signal = (object)($_POST);
        $signal->id = $tweeterer->addSignal(1, $signal);
        $tokens = $tweeterer->getTokens(1);
        $connection = new TwitterOAuth(
            API_KEY, 
            API_SECRET, 
            $tokens->access_token, 
            $tokens->access_token_secret);
        $connection->host = "https://api.twitter.com/1.1/";
        $ea = new EA();
        $message = "{$ea->get($signal->id_ea)->name} on $signal->symbol. $signal->operation at $signal->value";
        $connection->post('statuses/update', array('status' => $message));           
        echo '{"status": "ok", "message": {"text": "Signal processed."}}';
    }   
});

/**
 * REST implementation with TwitterOAuth.
 * Gives permissions to Laplacianlab's SDSS to tweet on the user's behalf.
 * Please, visit https://github.com/abraham/twitteroauth
 */
$app->get('/tweet-signals', function() use ($app) {   
    if (empty($_SESSION['twitter']['access_token']) || empty($_SESSION['twitter']['access_token_secret']))
    {
        $connection = new TwitterOAuth(API_KEY, API_SECRET);
        $request_token = $connection->getRequestToken(OAUTH_CALLBACK);
        if ($request_token)
        {
            $_SESSION['twitter'] = array(
                'request_token' => $request_token['oauth_token'],
                'request_token_secret' => $request_token['oauth_token_secret']
            );
            switch ($connection->http_code) 
            {
                case 200:
                    $url = $connection->getAuthorizeURL($request_token['oauth_token']);                    
                    // redirect to Twitter
                    $app->redirect($url);
                    break;
                default:
                    throw new Exception('Connection with Twitter failed.');
                break;
            }
        }
        else 
        {
            throw new Exception('Error Receiving Request Token.');
        }
    } 
    else 
    {    
        echo '{"status": "ok", "message": {"text": "Laplacianlab\'s SDSS can '
        . 'now access your Twitter account on your behalf. Please, if you no '
        . 'longer want this, log in your Twitter account and revoke access."}}';
    }    
});

/**
 * REST implementation with TwitterOAuth.
 * This is the OAuth callback of the method above. 
 * Stores the access tokens into the database.
 * Please, visit https://github.com/abraham/twitteroauth
 */
$app->get('/twitter/oauth_callback', function() use ($app) {
    if(isset($_GET['oauth_token']))
    {
        $connection = new TwitterOAuth(
            API_KEY, 
            API_SECRET, 
            $_SESSION['twitter']['request_token'], 
            $_SESSION['twitter']['request_token_secret']);
        $access_token = $connection->getAccessToken($_REQUEST['oauth_verifier']);
        if($access_token)
        {       
            $connection = new TwitterOAuth(
                API_KEY, 
                API_SECRET, 
                $access_token['oauth_token'], 
                $access_token['oauth_token_secret']);
            // Set Twitter API version to 1.1.
            $connection->host = "https://api.twitter.com/1.1/";
            $params = array('include_entities' => 'false');
            $content = $connection->get('account/verify_credentials', $params);    
            if($content && isset($content->screen_name) && isset($content->name))
            {
                $tweeterer = new Tweeterer();                
                $data = (object)array(
                    'twitter_id' => $content->id, 
                    'access_token' => $access_token['oauth_token'],
                    'access_token_secret' => $access_token['oauth_token_secret']);                
                $tweeterer->exists($content->id) 
                        ? $tweeterer->update($data) 
                        : $tweeterer->create($data);
                echo '{"status": "ok", "message": {"text": "Laplacianlab\'s SDSS can '
                . 'now access your Twitter account on your behalf. Please, if you no '
                . 'longer want this, log in your Twitter account and revoke access."}}';        
                session_destroy();
            }
            else
            {
                throw new Exception('Login error.');                
            }
        }
    } 
    else
    {
        throw new Exception('Login error.');
    }
});

/**
 * Run Slim!
 */
$app->run();

2.2. MySQL OOP Sarmalayıcıları

Şimdi Slim uygulamasının model dizininde PHP sınıfları Tweeterer.php ve EA.php oluşturmalıyız. Lütfen gerçek bir model katmanı geliştirmek yerine MySQL tablolarını basit nesne yönelimli sınıflara sarmalama yaptığımızı unutmayın.

model\Tweeterer.php:

<?php
require_once 'DBConnection.php';
/**
 * Tweeterer's simple OOP wrapper
 *
 * @author      Jordi Bassagañas
 * @copyright   2014 Jordi Bassagañas
 * @link        https://www.mql5.com/en/users/laplacianlab
 */
class Tweeterer
{   
    /**
     * @var string MySQL table
     */
    protected $table = 'twitterers';
    /**
     * Gets the user's OAuth tokens
     * @param integer $id
     * @return stdClass OAuth tokens: access_token and access_token_secret
     */
    public function getTokens($id)
    {
        $sql = "SELECT access_token, access_token_secret FROM $this->table WHERE id=$id";
        return DBConnection::getInstance()->query($sql)->fetch_object();        
    }    
    /**
     * Checks if it's been long enough so that the tweeterer can tweet again
     * @param string $timeLastTweet e.g. 2014-07-05 15:26:49
     * @param string $timeWindow A time window, e.g. 1 hour
     * @return boolean
     */
    public function canTweet($timeLastTweet=null, $timeWindow=null)
    {
        if(!isset($timeLastTweet)) return true;
        $diff = time() - strtotime($timeLastTweet);
        switch($timeWindow)
        {
            case '1 minute';                
                $diff <= 60 ? $canTweet = false : $canTweet = true;                
                break;                
            case '1 hour';                
                $diff <= 3600 ? $canTweet = false : $canTweet = true;                
                break;                
            case '1 day':                                
                $diff <= 86400 ? $canTweet = false : $canTweet = true;                
                break;
            default:                
                $canTweet = false;                
                break;                
        } 
        if($canTweet)
        {
            return true;
        }
        else 
        {
            throw new Exception('Please wait until the time window has elapsed.');
        }
    }
    /**
     * Adds a new signal
     * @param type $id_twitterer
     * @param stdClass $data
     * @return integer The new row id
     */
    public function addSignal($id_twitterer, stdClass $data)
    {
        $sql = 'INSERT INTO signals(id_ea, id_twitterer, symbol, operation, value) VALUES (' 
            . $data->id_ea . ","
            . $id_twitterer . ",'"    
            . $data->symbol . "','"
            . $data->operation . "',"
            . $data->value . ')'; 
        DBConnection::getInstance()->query($sql);        
        return DBConnection::getInstance()->getHandler()->insert_id;  
    }
    /**
     * Checks whether the given twitterer exists
     * @param string $id
     * @return boolean
     */
    public function exists($id)
    {
        $sql = "SELECT * FROM $this->table WHERE twitter_id='$id'";        
        $result = DBConnection::getInstance()->query($sql);        
        return (boolean)$result->num_rows;
    }    
    /**
     * Creates a new twitterer
     * @param stdClass $data
     * @return integer The new row id
     */
    public function create(stdClass $data)
    {
        $sql = "INSERT INTO $this->table(twitter_id, access_token, access_token_secret) "
            . "VALUES ('"
            . $data->twitter_id . "','"
            . $data->access_token . "','"
            . $data->access_token_secret . "')";        
        DBConnection::getInstance()->query($sql);
        return DBConnection::getInstance()->getHandler()->insert_id;
    }    
    /**
     * Updates the twitterer's data
     * @param stdClass $data
     * @return Mysqli object
     */
    public function update(stdClass $data)
    {
        $sql = "UPDATE $this->table SET "
            . "access_token = '" . $data->access_token . "', "
            . "access_token_secret = '" . $data->access_token_secret . "' "
            . "WHERE twitter_id ='" . $data->twitter_id . "'";        
        return DBConnection::getInstance()->query($sql);
    }    
    /**
     * Gets the last trading signal sent by the twitterer
     * @param type $id The twitterer id
     * @return mixed The last trading signal
     */
    public function getLastSignal($id)
    {
        $sql = "SELECT * FROM signals WHERE id_twitterer=$id ORDER BY id DESC LIMIT 1";
        $result = DBConnection::getInstance()->query($sql);
        if($result->num_rows == 1)
        {
            return $result->fetch_object();
        }
        else
        {
            $signal = new stdClass;
            $signal->created_at = null;
            return $signal;
        }
    }
}

model\EA.php:

<?php
require_once 'DBConnection.php';
/**
 * EA's simple OOP wrapper
 *
 * @author      Jordi Bassagañas
 * @copyright   2014 Jordi Bassagañas
 * @link        https://www.mql5.com/en/users/laplacianlab
 */
class EA
{   
    /**
     * @var string MySQL table
     */
    protected $table = 'eas';
    /**
     * Gets an EA by id
     * @param integer $id
     * @return stdClass
     */
    public function get($id)
    {
        $sql = "SELECT * FROM $this->table WHERE id=$id";     
        return DBConnection::getInstance()->query($sql)->fetch_object();
    }
}

model\DBConnection.php:

<?php
/**
 * DBConnection class
 * 
 * @author      Jordi Bassagañas
 * @copyright   2014 Jordi Bassagañas
 * @link        https://www.mql5.com/en/users/laplacianlab
 */
class DBConnection 
{ 
    /**
     * @var DBConnection Singleton instance
     */
    private static $instance;
    /**
     * @var mysqli Database handler
     */
    private $mysqli;
    /**
     *  Opens a new connection to the MySQL server
     */
    private function __construct()
    { 
        mysqli_report(MYSQLI_REPORT_STRICT);
        try {
            $this->mysqli = new MySQLI(DB_SERVER, DB_USER, DB_PASSWORD, DB_NAME); 
        } catch (Exception $e) {        
            throw new Exception('Unable to connect to the database, please, try again later.');
        }
    } 
    /**
     * Gets the singleton instance
     * @return type
     */
    public static function getInstance()
    {
        if (!self::$instance instanceof self) self::$instance = new self; 
        return self::$instance;
    } 
    /**
     * Gets the database handler
     * @return mysqli
     */
    public function getHandler()
    { 
        return $this->mysqli; 
    } 
    /**
     * Runs the given query
     * @param string $sql
     * @return mixed
     */
    public function query($sql)
    {
        $result = $this->mysqli->query($sql);
        if ($result === false)
        {       
            throw new Exception('Unable to run query, please, try again later.');
        }
        else
        {
            return $result;
        }
    } 
}

Sonuç

Bu makalenin ilk bölümünde tanıtılan SDSS'nin istemci tarafını geliştirdik ve sunucu tarafını bu karara göre şekillendirdik. Sonunda yeni MQL5'in yerel işleviWebRequest()'i kullandık. Bu spesifik çözümün artıları ve eksileri ile ilgili olarak, WebRequest()'in aslında web hizmetlerini tüketmek için değil, web sayfalarına GET ve POST talepleri yapmak için tasarlandığını gördük. Ancak aynı zamanda, sıfırdan özel bir bileşen geliştirmekten daha güvenli olduğu için bu yeni özelliği kullanmaya karar verdik.

MQL5 istemcisi ile PHP sunucusu arasında tamamen REST diyalogları kurmak daha zekice olurdu ancak WebRequest()'i belirli ihtiyacımıza uyarlamak çok daha kolay oldu. Böylece web hizmeti, URL kodlu verileri alır ve bunları PHP için yönetilebilir bir formata dönüştürür.

Şu anda bu sistem üzerinde çalışıyorum. Şimdilik kişisel alım satım sinyallerimi tweetleyebilirim. İşlevseldir, tek bir kullanıcı için çalışır, ancak gerçek bir üretim ortamında tamamen çalışabilmesi için bazı eksik parçalar vardır. Örnek olarak Slim, veritabanından bağımsız bir yapıdır, bu nedenle SQL enjeksiyonlarını önemsemelisiniz. MetaTrader 5 terminali ile PHP uygulaması arasındaki iletişimin nasıl güvenli hale getirileceğini de açıklamadık, bu yüzden lütfen bu uygulamayı bu makalede sunulduğu gibi gerçek bir ortamda çalıştırmayın.

MetaQuotes Ltd tarafından İngilizceden çevrilmiştir.
Orijinal makale: https://www.mql5.com/en/articles/1044

Ekli dosyalar |
database.txt (1.32 KB)
dummy_ontimer.mq5 (1.36 KB)
Dummy.mq5 (3.27 KB)
index.txt (6.08 KB)
Tweeterer.txt (4.59 KB)
EA.txt (0.58 KB)
DBConnection.txt (1.54 KB)
Johnpaul77 Sinyal Sağlayıcıları: "Stratejimiz Üç Yıldan Fazla süredir Kar Getiriyor. O Zaman Neden Değiştirelim?" Johnpaul77 Sinyal Sağlayıcıları: "Stratejimiz Üç Yıldan Fazla süredir Kar Getiriyor. O Zaman Neden Değiştirelim?"
Küçük bir sır verelim: MQL5.com web sitesi ziyaretçileri, zamanlarının çoğunu Johnpaul77 sinyalinin sayfasında geçirir. Gerçek hesaplarda toplam 5,7 milyon dolarlık fonla yaklaşık 900 abonesi olan sinyal derecelendirmemizin lideridir. Sinyalin sağlayıcılarıyla görüştük. Görünüşe göre, onlardan dört tane var! Ekip üyeleri arasında görevler nasıl dağıtılır? Hangi teknik araçları kullanıyorlar? Neden kendilerine John Paul diyorlar? Ve son olarak, Endonezya'daki sıradan oyuncular nasıl MQL5.com'da en iyi sinyal sağlayıcılar haline geldi? Tüm bunları makalede öğrenin.
MQL5.com Freelance: Geliştiricilerin Gelir Kaynağı (Veri Grafiği) MQL5.com Freelance: Geliştiricilerin Gelir Kaynağı (Veri Grafiği)
MQL5 Freelance Hizmeti dördüncü yılı vesilesiyle, var olan tüm zamanların hizmet sonuçlarını gösteren bir veri grafiği hazırladık. Rakamlara ne hacet: Bugüne kadar toplam yaklaşık 600.000 $ değerinde 10.000'den fazla talimat gerçekleştirildi ve 3.000 müşteri ve 300 geliştirici hizmeti çoktan kullandı.
Optimizasyon. Birkaç Basit Fikir Optimizasyon. Birkaç Basit Fikir
Optimizasyon süreci, bilgisayarın veya MQL5 Bulut Ağı test aracılarının önemli kaynaklarını gerektirebilir. Bu makale, MetaTrader 5 Strateji Test Cihazının işini kolaylaştırmak ve geliştirmek için kullandığım bazı basit fikirleri içermektedir. Bu fikirleri belgelerden, forumlardan ve makalelerden aldım.
MQL5.com'da Freelance İşler - Geliştiricinin Favori Yeri MQL5.com'da Freelance İşler - Geliştiricinin Favori Yeri
Artık alım satım robotu geliştiricilerinin hizmetlerini Expert Advisors ihtiyacı duyan yatırımcılara pazarlamasına gerek yok - şimdi onlar sizi bulacaklar. Halihazırda binlerce yatırımcı, MQL5 Freelance geliştiricilerine talimat verip ve MQL5.com adresinde çalışmak için ödeme yapmaktadır. Bu hizmet 4 yıl boyunca, üç bin yatırımcının gerçekleştirilen 10.000'den fazla iş için ödeme yapmasını kolaylaştırdı. Yatırımcıların ve geliştiricilerin faaliyetleri sürekli büyüyor!