问题:目前,臭名昭著的TSLAB是俄罗斯股票市场上用于交易自动化的最方便,最完整的软件(以下简称软件)。
尽管方便的可视化编辑器在编写交易脚本方面具有无可置疑的优势,即使您不具备编程语言知识,也可以使您编写机器人,但仍存在许多弊端,这对我来说使使用该软件极为无利。 而且我认为,不仅对我而言,考虑到在Mosbirz的帐户平均规模通常不超过50万卢布。
1. 费用:每月4500卢布的订阅费+虚拟服务器的租金(每月1000卢布)
这笔固定成本给我的交易带来了沉重的负担。 因此,拥有50万卢布的帐户规模 并希望每年从中获得至少20%的收益,而使用现有成本,您需要赚取约32-35%的收益才能达到计划的获利能力。
2. 工作的不稳定性:尽管我的算法主要处理市场订单(订单类型,假定执行率为100%),但我的头寸经常翻倍或根本没有执行。
目标:编写软件以实现自动交易以最大程度地减少固定成本,并具有方便的界面来创建交易脚本,使您无需编写任何编程知识即可编写交易机器人。
下图显示了整个项目的架构,包括当前和功能以及计划中的改进。

该程序中最重要的链接无疑是Tradingview网站(以下简称为TV)。 由于内置的Pine_Script语言,它只是为我们提供了编写交易脚本的便捷功能。
顺便说一句,该语言不需要特定的知识,并且基本上类似于Metastock软件包的“易用语言”,并且俄语中的在线帮助使编写代码变得尽可能愉快。
突破移动平均线的策略示例(字面上为三行代码):
mov_average=sma(close,x) strategy.entry("My Long Entry Id", strategy.long,when=close>mov_average) strategy.entry("My Short Entry Id", strategy.short,when=close<mov_average)
现在有了编写交易脚本的便捷界面,剩下的工作就是建立将应用程序从电视直接发送到交易系统(在我的例子中是Quik)或直接发送到经纪人服务器的过程。 唯一的问题是电视没有用于实现此功能的开放API。
为了解决这个问题,我想到的第一件事就是使用一个插件来测试WebSelenium,并通过搜索XPath定位器来找到我们需要负责买卖信号的元素。
信号本身显示在表格中,应该没有任何问题。 但是要搜索极端信号,该表需要滚动,但是我找不到滚动元素(请参见下图)。

因此,我不得不寻找另一种解决方案。
在视觉上,电视信号显示在canvas元素中。 如有必要,可以更改信号颜色(例如:红色出售,绿色购买)。
我们在应用程序中设置的电视中设置的颜色。 应用程序本身是用Java编写的,图形界面是使用Swing库实现的
在程序本身中,我们需要选择画布区域(或仅是扫描区域),在其中我们将寻找控制颜色。
下图显示了Tradingview网站,其中包含三种选择的工具,每种工具都设置了交易信号的颜色。 这些颜色在我的Parse_Signal程序中重复。
。
设置扫描区域并设置要交易的工具的类型后(顺便说一句,程序设置需要5分钟,并保存在扩展名为.txt的文件中)。 接下来,按“开始”按钮,程序开始工作。
它在两个线程中工作。
1个第一个线程:
扫描所选区域(在这种情况下为画布)。
我们通常使用Robot类的功能进行扫描:
BufferedImage buf= robot.createScreenCapture(new Rectangle(selection.x, selection.y, selection.width, selection.height))
接下来,它将结果扫描分为像素阵列:
int[] pixels = copyFromBufferedImage(buf);
在像素阵列中搜索交易信号的控制颜色。 从左到右搜索。 即 与程序相关的是最右边像素的颜色:
for(int i=0;i<pixels.length;i++ ) { if (pixels[i] == (buy.getBackground().getRGB() & 0xFFFFFF)) { position = 1;

根据模板记录贸易记录(在扩展名为.tri的文件中),具体取决于找到的颜色。 实际上,在这里,Quik交易终端中的一切都很简单,可以自动从文件中读取交易。 对于我们来说,按照某种模式注册它们就足够了。 当出现新条目时,快速查询将请求发送到代理的服务器。 每500毫秒读取一次文件。 关于交易信号的信息可以选择发送到邮件,电话或交易系统(可以同时选择三个参数)。 1个线程的工作频率为500毫秒。
if (position==1&&status!=1&&b1==1) { if(dialog.isSend_phone()==true) { new SMS().sendSms(dialog.getPhone(), "TS_1: "+ (String)dialog.cbFirst.getSelectedItem()+" "+price+" "+new Date(), "TEST-SMS", dialog.getLogin(), dialog.getPassword());} if(dialog.isSend_trade()==true){ tr.Order_Buy();} if(dialog.isSend_mail()==true){ test.sendSignal("BUY","TS_1: Buy in signal at price "+ (String)dialog.cbFirst.getSelectedItem()+" "+price+" "+new Date());} status = 1;} ...................... ......................
2程序流程通过解析Finam站点的html页面执行对交易工具价格的请求。 使用了JSOUP插件。 在这里,我只需上传html页面并搜索所需交易工具的代码(例如:Si,Sber等)。
public void run() { while (true) { Document doc = null; Document doc_2 = null; try { doc = Jsoup.connect("https://www.finam.ru/quotes/futures/moex/").get(); doc_2 = Jsoup.connect("https://www.finam.ru/profile/mosbirzha-fyuchersy/sbrf").get();} catch (IOException e) { e.printStackTrace(); continue;} StringBuffer buffer = new StringBuffer(doc.text()); StringBuffer buffer_2 = new StringBuffer(doc_2.text()); Map<String, String> map = new HashMap<>() try {map.put(elements[1], buffer.substring(buffer.indexOf("Si "), buffer.indexOf("Si ") + 8).split("Si ")[1]); map.put(elements[2], buffer.substring(buffer.indexOf("RTS "), buffer.indexOf("RTS ") + 10).split("RTS ")[1]); map.put(elements[3], buffer.substring(buffer.indexOf("LKOH "), buffer.indexOf("LKOH ") + 10).split("LKOH ")[1]); map.put(elements[4], buffer.substring(buffer.indexOf("BR "), buffer.indexOf("BR ") +8).split("BR ")[1]); map.put(elements[5], buffer.substring(buffer.indexOf("GAZP "), buffer.indexOf("GAZP ") + 10).split("GAZP ")[1]); map.put(elements[6], buffer.substring(buffer.indexOf("GOLD "), buffer.indexOf("GOLD ") + 11).split("GOLD ")[1]); map.put(elements[7], buffer.substring(buffer.indexOf("MOEX "), buffer.indexOf("MOEX ") + 10).split("MOEX ")[1]); map.put(elements[8], buffer.substring(buffer.indexOf("MIX "), buffer.indexOf("MIX ") + 10).split("MIX ")[1]); map.put(elements[9], "0"); map.put(elements[10], buffer_2.substring(buffer_2.indexOf(" "), buffer_2.indexOf(" ") + 23).split(" ")[2] + buffer_2.substring(buffer_2.indexOf(" "), buffer_2.indexOf(" ") + 23).split(" ")[3]);} catch (Exception e) { System.out.println(e); text.setText(" "); continue;} price = String.valueOf((int) Double.parseDouble(map.get((String)
显然,这是程序中的薄弱环节,因为对html页面的任何更改都将引发Exception。 因此,将来计划请求直接通过Quik或直接从代理服务器请求交换信息。
为此,您可以使用现成的.dll库在C#中使用Quik,但是由于我是用Java编写的,因此在我的情况下,使用lua(内置Quik语言)实现脚本会更容易,该脚本会将买卖价格记录在一个单独的文件中,该程序然后读取Parse_Signal。
应该注意的是,实际上我们得到的是一堆相当庞大的TV + Parser + Quik。 尽管该解决方案具有稳定性,但将来仍计划将应用程序不发送到Quik,而是直接发送到代理服务器( 例如:使用Alora的Atlentis接口作为选项 )。 该库确实再次使用C#实现,因此您必须提出一些建议。
该程序使我能够解决自己为自己设置的初始任务:
即有时减少固定成本。
程序代码已发布在公共领域。
如果有人准备分享与电视互动的想法,我会很高兴在评论中看到这一点。