Sosyal Teknoloji Girişimi Kurma, Bölüm II: Bir MQL5 REST İstemcisi Programlama
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!
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 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ı
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
- Ücretsiz ticaret uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz