📜 ⬆️ ⬇️

Automation of stock trading on the MICEX on the example of the terminal from Alfa Bank

In my free time I am engaged in the creation of trading robots. I have been interested in the topic of financial markets and automation of trading for a long time, and today I am happy to share an example of creating a simple robot using the example of the well-known exchange terminal from Alfa Bank.

Prehistory


Many banks (and other companies) now provide brokerage services , which means that by concluding an additional agreement with the bank (except for the main one), the client can invest their savings in various financial instruments. Quite a long time ago, trading terminals appeared - programs through which a bank customer, having passed authorization, can submit bids, buy and sell. For example, stocks, futures, options.

Since the market is in constant motion, prices change. By selling or buying a tool at the right time, you can earn on the difference in rates. In order for a person not to have to constantly be at the computer and monitor the bidding process, robots programs are developed that work according to a predetermined algorithm — place orders for buying and selling, monitor the balance in accounts, and evaluate the market situation. Such robots are set up initially and then only occasionally are adjusted by humans, ideally, of course. In fact, everything is much more complicated.

System description


The idea of ​​connecting to various trading terminals is not new at all, but is ideal for automating user actions in client banking software. Despite the fact that I now have direct access to the Moscow Exchange using FIX / FAST protocols, I check all trading strategies through a bank terminal, which I want to show in this article using the example of the Alfa Direct Version 3.5 terminal.
')
Roughly speaking, the task boils down to the following (in steps):

I want to note that the existing solution is constantly being developed by me. Guided by the principle “the simpler, the better,” I exclude much that was added earlier (as unnecessary). Therefore, now the system, for example, is able to set only limited orders that are required by my trading strategies. Everything else is easily added as needed.

Interface to interact with the banking terminal

To add a new communication method, you need to implement the following interface:

public interface IIntegrator { bool Connected {get; set;} void Initialize(); void Connect(string userName, string password); void Disconnect(); void Stop(); Action UpdateInfo { get; set; } bool AllowsHistory { get; } bool AllowsTesting { get; } bool AllowsTrading { get; } bool RequiresCredentials { get; } string[] AvaliableMarketCodes { get; } //Date, Open, High, Low, Close, Volume, Adj Close List<string[]> LoadHistory(string marketNameCode,string instrument,int period,DateTime dateFrom,DateTime dateTo); Helpers.Positions[] GetPositions(); double GetLastPrice(string code); int LimitSell(string briefCase, string ticker, string placeCode, int amount, double price, int timeOut, double? activateIfPriceHasGrown, double? activateIfPriceHasFallen); int LimitBuy(string briefCase, string ticker, string placeCode, int amount, double price, int timeOut, double? activateIfPriceHasGrown, double? activateIfPriceHasFallen); void DropOrder(int orderNo); } 

In addition to downloading historical data, connecting to the server and issuing orders, this interface implements read-only logical values, which allow the program to understand whether the plug-in allows downloading history, checking strategies on historical data, and, actually, trading (see the figure below).

For example, the banking terminals Quick and Alpha Direct may place bids, but I have a module that only downloads historical data from one of the well-known stock exchange websites. Naturally, such an application module can not exhibit.

UpdateInfo () is called in case of changes in quotes, balance and any other data, this allows the program to update the information it receives from the connected module.

The additional class Positions describes the current positions for each of the market instruments - by portfolios, markets, tickers, in fact, balance, profits and losses per day.

 public class Positions { public string Briefcase { get; set; } public string MarketCode { get; set; } public string Ticker { get; set; } public double Amount { get; set; } public double DailyPL { get; set; } } 

Getting positions and historical data

Below is the implementation of the class for interacting with the Alpha Direct Version 3.5 terminal; the Interpop.ADLide COM module must be added to the project references:

Source code for Alpha Direct 3.5
  public class AlfaDirectIntegrator : IIntegrator, INotifyPropertyChanged { public bool Connected { get { if (_terminal == null) return false; return _terminal.Connected; } set { _terminal.Connected = value; OnPropertyChanged("Connected"); if (UpdateInfo!=null) UpdateInfo(); } } public Action UpdateInfo { get; set; } private AlfaDirectClass _terminal; public bool AllowsHistory { get { return true; } } public bool AllowsTesting { get { return true; } } public bool AllowsTrading { get { return true; } } public bool RequiresCredentials { get { return true; } } //        OnConnectionChanged //       //           private Timer _connectionStateChecker; private int _msToCheckConnectionState = 1000; //     public void Initialize() { _terminal = new AlfaDirectClass(); _connectionStateChecker = new Timer(_msToCheckConnectionState); _connectionStateChecker.Elapsed += _connectionStateChecker_Elapsed; _terminal.OnConnectionChanged += (obj) => { _connectionStateChecker_Elapsed(null, null); }; _connectionStateChecker.Enabled = true; } void _connectionStateChecker_Elapsed(object sender, ElapsedEventArgs e) { OnPropertyChanged("Connected"); UpdateInfo(); } public void Connect(string userName, string password) { _terminal.UserName = userName; _terminal.Password = password; Connected = true; } public void Disconnect() { Connected = false; } public void Stop() { if (_connectionStateChecker != null) { _connectionStateChecker.Enabled = false; _connectionStateChecker.Elapsed -= _connectionStateChecker_Elapsed; _connectionStateChecker = null; } _terminal = null; } public string[] AvaliableMarketCodes { get { return MarketCodes.Keys.ToArray(); } } private Dictionary<string, string> MarketCodes = new Dictionary<string, string>() { { " ", "MICEX_SHR_T" }, { "", "FORTS" }, { " ", "MICEX_SHR"}, { "FOREX", "CURRENCY"}, { "DJ Indexes", "DJIA"}, { " ", "EBONDS"}, { "OTC EUROCLEAR", "EUROTRADE"}, { ". ", "INDEX"}, { ". ", "INDEX2"}, { "IPE", "IPE"}, { "LME", "LME"}, { "LSE", "LSE"}, { "LSE(delay)", "LSE_DL"}, { " ", "MICEX_BOND"}, { " ", "MICEX_EBND"}, { " ", "MICEX_SELT"}, { "NEWEX", "NEWEX"}, { "-", "NONMARKET"}, { "- ()", "NONMARKET_DCC"}, { "NYSE", "NYSE"}, { " ()", "OTC_NDC"}, { " ()", "RTS_GAZP"}, { " ", "RTS_SGK_R"}, { "", "RTS_SHR"}, { " .", "RTS_STANDARD"} }; public List<string[]> LoadHistory(string marketNameCode, string instrument, int period, DateTime dateFrom, DateTime dateTo) { //  : 08.07.2010 15:22:21 //     marketNameCode = MarketCodes[marketNameCode]; List<string[]> data = new List<string[]>(); var rawData = (string)_terminal.GetArchiveFinInfo(marketNameCode, instrument, period, dateFrom, dateTo.AddDays(1), 2, 100); if (_terminal.LastResult != StateCodes.stcSuccess) { System.Windows.MessageBox.Show(_terminal.LastResultMsg,"",System.Windows.MessageBoxButton.OK,System.Windows.MessageBoxImage.Error); return data; } string[] stringSeparators = new string[] { "\r\n" }; //    var strings = rawData.Split(stringSeparators,StringSplitOptions.None).ToList(); foreach (var s in strings) if (s != "") { var values = s.Replace(',','.').Split('|'); data.Add(new string[] {values[0] , values[1], values[2], values[3], values[4], values[5], values[4] }); } //   return data; } public double GetLastPrice(string code) { var message = _terminal.GetLocalDBData("FIN_INFO", "last_price", "p_code like \'" + code + "\'"); if (message == null) return 0; return Convert.ToDouble(message.Split('|')[0]); } public int LimitSell(string briefCase, string ticker, string placeCode, int amount, double price, int timeOut, double? activateIfPriceHasGrown, double? activateIfPriceHasFallen) { return _terminal.CreateLimitOrder(briefCase, placeCode, ticker, DateTime.Now.AddMinutes(1), " Trade Robot System", "RUR", "S", amount, price, activateIfPriceHasGrown, activateIfPriceHasFallen, null, null, null, 'Y', null, null, null, null, null, null, null, null, null, null, timeOut); } public int LimitBuy(string briefCase, string ticker, string placeCode, int amount, double price, int timeOut, double? activateIfPriceHasGrown, double? activateIfPriceHasFallen) { return _terminal.CreateLimitOrder(briefCase, placeCode, ticker, DateTime.Now.AddMinutes(1), " Trade Robot System", "RUR", "B", amount, price, activateIfPriceHasGrown, activateIfPriceHasFallen, null, null, null, 'Y', null, null, null, null, null, null, null, null, null, null, timeOut); } public void DropOrder(int orderNo) { _terminal.DropOrder(orderNo, null, null, null, null, null, 0); } public Helpers.Positions[] GetPositions() { var result = new List<Helpers.Positions>(); var message = _terminal.GetLocalDBData("balance", "acc_code, p_code, place_code, forword_rest, daily_pl", ""); if ((message == null)||(!Connected)) return result.ToArray(); string[] stringSeparators = new string[] { "\r\n" }; //    var strings = message.Split(stringSeparators,StringSplitOptions.None).ToList(); foreach (var str in strings) if (str != "") { var fields = str.Split('|'); result.Add(new Helpers.Positions() { Briefcase = fields[0], Ticker = fields[1], MarketCode = fields[2], Amount = Convert.ToDouble(fields[3]), DailyPL = Convert.ToDouble(fields[4]) }); } return result.ToArray(); } #region PropertyChanged members public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion } 


In fact, market codes can be obtained through the terminal itself. I had problems with handling OnConnectionChanged events, so I additionally had to use a timer.

Example of loading historical data


At the bottom, different colors (depending on profit or loss for the day) show assets - stocks and money.


Strategy checking and trading

The trading strategy accepts data on the state of the market, conducts analysis and, as a result, makes a purchase, sale or stays idle, waiting for the best moment to enter the market.

For obvious reasons, here I can’t give the source code, but I’ll say that at each moment in time the strategy receives information on current positions and any quotes for any periods of time, as well as on all placed purchase and sale requests. Conducts trend analysis using different indicators. After that, the decision is made - to buy, sell or wait.

The result was a scheme of a simple trading robot, where you can check any trading strategy. When testing on historical data, do not forget about the brokerage commission.

Thanks for attention.

If it was interesting, next time I can tell you about regression analysis, econometrics, some non-standard trend indicators, how to get stock data from the Internet, about direct connection to exchanges and FIX / FAST protocols.

Source: https://habr.com/ru/post/253839/


All Articles