[原创]【技术贴】ecn平台解决滑点,成交失败,点差扩大的问题
本帖最后由 张翠山 于 2016-6-22 15:30 编辑
这三个问题我早想着手解决了,但一直懒,最近为了应付公投带来的异常波动,也是拼了。
已经很久没谈过什么外汇的东西了,到处灌水八卦挑事。 这个就顺手写出来,算是一种赎罪。
这三个问题的根本解决方案是用挂单替代市价单。
这些问题在mm平台是无法解决的。
mm平台没有市场深度,没有真实成交,买卖报价就是两个数字在跳来跳去逗你玩
开仓平仓是市价单,条件订单(buy stop,buy limit,sell stop,sell limit)也都是达到条件后发送一个市价单。
只要是市价单,就可能会遭遇一切不公正的待遇。
ecn平台最大的好处是,每个单子都是真实成交的,都有相应的对手盘。
可以理解成,ecn平台的外汇交易和A股的股票交易是完全一样的。
你可以把单子挂到ecn网络上,等待别人摘取。想想股票挂单为什么不会滑点,成交失败。 ecn同理。
举例来说,买1是100,你在80的价位挂了一手买单,那么只要卖1碰触了79.9,那么你的单子一定是会得以执行,
且完整执行的(部分成交都不会出现),且价格一定是80(无滑点)。
但当你看到报价在80的时候下一个买入的市价单,那么实际成交价格可能就是81,
在剧烈运动的时候,85,90都有可能。当然很少见。
挂单和市价单各有利弊,市价单,保证成交,但不保证价格,挂单,保证价格,但不保证成交。
对于小资金来说成交根本不用考虑,价格才是重中之重。
即使对于大资金,我认为价格也比成交重要很多。
于是我开始把所有市价单的指令全部用挂单替换。开仓并不复杂,但平仓是个问题。
因为平仓也是按照市价单执行,我解决平仓的方法是,用反向的挂单开一个大小相同的反向单
但这个单子完全成交后,把两个仓位合并,形成一次平仓。然后再在同样的位置挂一个单,
如果再成交了就形成真正的开仓。
我会把我平仓并反手的代码示例贴出来,给将来也要着手替换的人做一个参考。
代码是杜卡斯贝版本的,理论上所有的ecn平台都能挂单,具体代码要看相应文档了。
以下是一个每个tick都不停执行的函数,负责把我应持有的方向和真实持仓对应上,
注释解释很多事情,就不多说了,mm平台玩家可以直接跳过。
void syncreal(ITick tick) {
if (budui==1) return;
List orders = new ArrayList();
try {
/* jilu[0] 代表现在应该持有单子的方向,0为多单,1为空单
* jilu[1] 代表应该持有单子的价位
* jilu[2] 代表应该持有单子的手数
*/
if (jilu[0]!=chiyou) {
// 当方向发生变化,把应当持有单子的方向和价格记录下来,买单挂在bid往上0.1点的位置(强制点差为0.1),卖单挂在bid价
jilu[0] = chiyou;
if (jilu[0]==0)
jilu[1] = tick.getBid() + Instrument.EURUSD.getPipValue() / 10;
else if (jilu[0]==1)
jilu[1] = tick.getBid();
else
jilu[1] = -1;
jilu[2] = -1;
log.p2(" (需要反手,新的方向为"+new String(jilu[0]==0?"多单":"空单")+",位置锁定在"+String.format("%.5f", jilu[1])+")");
} else {
// 如果方向没有变化,检查一下,如果当前持有单子是成交的且成交数量和预设的相同,那么直接返回
orders = engine.getOrders();
if (orders.size()==1) {
IOrder o = engine.getOrders().get(0);
if (o.getState()==IOrder.State.FILLED && o.getAmount()==jilu[2])
return;
}
}
orders = engine.getOrders();
for (IOrder o : orders) {
if (o.getState()==IOrder.State.CREATED || o.getState()==IOrder.State.OPENED) {
if ((o.isLong()&&jilu[0]==1) || (!o.isLong()&&jilu[0]==0)) {
// 检查所有已经挂好的单子,如果现在是要开多单,那么存在的挂好的空单一定是上次交易没得到成交遗留下来的,先把他们都清楚
o.close();
o.waitForUpdate(1000, State.CANCELED, State.CLOSED);
log.p2(" (取消上次没有成交的挂单,如果上次该平的没平,这次就不用再开新的,)");
} else if ((o.isLong()&&jilu[0]==0) || (!o.isLong()&&jilu[0]==1)) {
// 如果是同向的挂单,那我们直接返回,等待他们得以执行
return;
}
}
}
// 接下来要平掉之前相反的单子,但我先检查一下是不是有两个同时已经成交的单子,有就合并一下。这种情况正常来说上不应该出现,所以我处理也不会很严谨
orders = engine.getOrders();
if (orders.size()>=2 && orders.get(0).getState()==State.FILLED && orders.get(1).getState()==State.FILLED) {
//if (orders.get(0).isLong()==orders.get(1).isLong())
log.p2(" (不知为何有两个成交单子,先将其合并)");
//else if (orders.get(0).getAmount()==orders.get(1).getAmount())
IOrder o = engine.mergeOrders("shenqi", orders.get(0), orders.get(1));
o.waitForUpdate(1000);
}
// 现有的已成交的单子,如果是逆向的,我们要平仓
orders = engine.getOrders();
for (IOrder o : orders) {
if (o.getState()==IOrder.State.FILLED) {
if ((o.isLong()&&jilu[0]==1) || (!o.isLong()&&jilu[0]==0)) {
// 不能简单的去o.close(), 我们要用挂单去保证精确点位成交。 当收到挂单成交的消息后,我们再将其合并。
if (o.isLong())
engine.submitOrder("pingcang", Instrument.EURUSD, OrderCommand.PLACE_OFFER, o.getAmount(), jilu[1], 0, 0, 0, 0);
else
engine.submitOrder("pingcang", Instrument.EURUSD, OrderCommand.PLACE_BID, o.getAmount(), jilu[1], 0, 0, 0, 0);
log.p2(" (检测到已存在的"+new String(o.isLong()?"多单":"空单")+",挂上反向同大小的"+new String(o.isLong()?"空单":"多单")+",等待成交后将二者合并)");
return;
}
}
}
// 如果此时我们没有任何单子,我们选择开仓,其实在message里应该已经开了,这里不应该得到执行,只为保险起见
orders = engine.getOrders();
if (orders.size()==0) {
// 根据当前净值计算出开仓的手数
if (jilu[2]<=0) {
int tmp = (int)(account.getEquity() / 200);
double amount = tmp * 0.001;
jilu[2] = amount;
}
// 根据我们应该开的方向,发送相应的挂单
if (jilu[0]==1)
engine.submitOrder("no3sell", Instrument.EURUSD, OrderCommand.PLACE_OFFER, jilu[2], jilu[1], 0, 0, 0, 0);
else if (jilu[0]==0)
engine.submitOrder("no3buy", Instrument.EURUSD, OrderCommand.PLACE_BID, jilu[2], jilu[1], 0, 0, 0, 0);
log.p2(" (放置"+new String((jilu[0]==1)?"卖出":"买入")+"挂单,位置"+String.format("%.5f", jilu[1])+",手数"+jilu[2]+")");
}
} catch (JFException e) {
console.getOut().println(e);
log.p(e.toString());
}
}
下面这个函数是收到消息时候的处理。
因为挂单的成交时间是不确定的,所以要在message里拿,拿到再往下进行。
public void onMessage(IMessage message) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdf8 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf8.setTimeZone(TimeZone.getTimeZone("GMT+8"));
IOrder order = message.getOrder();
switch(message.getType()){
case ORDER_SUBMIT_OK :
break;
case ORDER_SUBMIT_REJECTED :
console.getOut().println("Order open failed: " + message.getOrder());
log.p("ORDER_SUBMIT_REJECTED");
break;
case ORDER_CHANGED_OK:
case ORDER_FILL_OK :
log.p("order open [open fx:"+ (order.isLong()?0:1) +"] [open price:"+ order.getOpenPrice() +"] [open time:"+ sdf.format(new Date(order.getFillTime())) +
"] [open amount:"+ order.getAmount() +"]");
if (message.getReasons().contains(IMessage.Reason.ORDER_FULLY_FILLED)) {
List orders = new ArrayList();
orders = engine.getOrders();
// 只有一个单说明开单完毕,什么都不用做了
if (orders.size()==1) {
log.p2("开"+new String(order.isLong()?"多":"空")+"仓,位置"+order.getOpenPrice()+",手数"+order.getAmount()*10+",开仓花费佣金-"+order.getCommissionInUSD()+"美元 ");
return;
}
// 从现存的单子找出反向同等大小的两个,合并到一起,至此平仓工作完成
IOrder o1 = null;
for (IOrder o : orders) {
if (o.getState()==State.FILLED && o.isLong()!=order.isLong() && o.getAmount()==order.getAmount()) {
log.p2("全平仓,价格"+ order.getOpenPrice() +",这笔利润"+ o.getProfitLossInUSD() +"美元,相当于"+ o.getProfitLossInPips() +"点,平仓花费佣金-"+order.getCommissionInUSD()+"美元,当前账户净值是"+ account.getEquity() +"美元");
o1 = engine.mergeOrders("closeall", order, o);
o1.waitForUpdate(1000, State.CLOSED);
break;
}
}
// 如果此时我们没有任何单子,我们选择开仓
orders = engine.getOrders();
if (orders.size()==0) {
// 根据当前净值计算出开仓的手数,别忘记在jilu[2]里放一份
int tmp = (int)(account.getEquity() / 200);
double amount = tmp * 0.001;
jilu[2] = amount;
// 根据我们应该开的方向,发送相应的挂单
if (jilu[0]==1)
engine.submitOrder("no3sell", Instrument.EURUSD, OrderCommand.PLACE_OFFER, jilu[2], jilu[1], 0, 0, 0, 0);
else if (jilu[0]==0)
engine.submitOrder("no3buy", Instrument.EURUSD, OrderCommand.PLACE_BID, jilu[2], jilu[1], 0, 0, 0, 0);
log.p2(" (放置"+new String((jilu[0]==1)?"卖出":"买入")+"挂单,位置"+String.format("%.5f", jilu[1])+",手数"+jilu[2]+")");
}
}
break;
代码改完测,测完改,暂时可以跑。但肯定不严谨且有很多没处理的地方
如果挂单不成交,我处理的办法是,好比本来多单,我想反手,
正常逻辑:挂空单-》成交-》合并平仓-》再挂空单-》成交
挂单未成交逻辑:挂空单-》迟迟不成交,等来了下次反手,也就是又要开多了-》之前挂的空单全部撤掉,保留已经开好的多单不变。
这个也就算了,但是最严重的是,所有部分成交我都没有处理
好比持有10手多单,我挂了10手空单,成交了3手,那么我同时拥有3手空单,10手多单,7手挂单,
还有可能是挂的10手空单全成交,顺利平仓,接下来开仓的10手挂单部分成交。
那么我看上去是开好空单的状态,实际上开的手数不够。
这些情况想着就烦,我脑容量不够,就先不处理了。实盘遇到再说。
关于解决点差扩大问题,用挂单可以实现固定点差,比如我就是买入的挂单位置放在bid+0.1上
那么只要执行了,就是0.1的点差,弊端依然是牺牲执行的可能性。
最后我想再谈两件事,第一件是我这么做是追求严谨实则闲的蛋疼还是真的很重要。
我先说个我的观点,交易一万次后,你的平均每单利润,永远是你交易成本那个级别的
好比交易成本是2,普通人每单利润扣除成本是-1,厉害点的人是0.5,高手能到3,大神可能到6!
但再神,也不能到60.
而且无论你做短线还是长线。 即使你是只做500点以上的月线波段,你看不起2个点左右的交易成本
我还是说你1w单长线之后,平均盈利依然是2个点上下不远。
所以我们每个人都是交易成本敏感型。只是不用ea的人,感觉不出来而已。
我每单输赢多在60点上下,但我回测9000多单显示,我平均每单盈利3点左右。
当初我哭着喊着从oa转到du,就是因为我清楚知道自己是交易成本敏感型(别笑,你也是)。
每次的滑点也是交易成本级别的,你利润也是这个级别的,所以肉眼看来可以忽略的小差别,
在一万次执行后,将是巨大的损失,尤其对于复利的系统,那就更无法估算了。
第二件事,简单谈下高频。了解高频不是为了用其赚钱,咱们没那条件
了解他要知道他怎么把你钱黑走的,好做一些基本的防备。
钱少无所谓,下大单,不了解这个就是死路一条。
你检测到卖1是100手挂单,于是下100手市价单,寄希望于在卖1位置全部成交
当你发出这个指令,高频逼们就会在网络上侦测到,然后迅速把卖1的100手吃掉,
并在本来卖2的位置上挂上100手卖单,于是你就买贵了。
这要跟菜市场,我看到白菜卖10元,我掏出10元去买,一个高频逼突然跑我前面把10元的白菜买了
然后要11元卖给我,我大可以拒绝,我说刚才还10元呢,贵一分我不要。
可惜外汇你指令发出去,执行之前可没人跟你确认,别说贵一元,贵10元,你也不能反悔了。
你一个大户,每次都买贵,一卖就卖便宜,都是交易成本一级的变动,你日子还过不过了。
所以对付这群高频逼们的原则就是,永远不用市价单! 只用挂单成交
我10元放在那里,谁给我白菜谁拿走!
当然,还是资金少怎么都好说,你要是想买1000手,你挂在那里试试
被另一伙高频逼们看到。他们直接在11元买货,反正你有你托市,不愁砸手里
于是你看到11 12 这些价位频繁投机倒把,你1000手的10元挂单只能看着。
那怎么办,应对方法是,先挂100手,成交了再同样价位挂100手,拆开了买。
但你不管怎么弄,高频逼总有办法反制你。 可以去看冰山订单的文章,里面说的比较详细
总之下单是一件很难的事情。要和躲在网络阴暗处的高频逼们对抗需要很多经验和智慧。
我们普通人关注策略比较多,关注下单的太少,
但用挂单替代市价单这种最基本的还是要做的。其余的看机缘,都可以讨论。
发表于:2016-06-22 07:53只看该作者
2楼
用挂单解决滑点确实不错.
有朋友在其他平台试过么,你们用挂单的时候有没有出现过挂单滑点的呢.
解决了滑点问题,对日内短线是最有价值的一件事情.
点评
发表于 2016-06-22 08:04
韬客社区www.talkfx.co
3楼
sanjiang 发表于 2016-6-22 15:53
用挂单解决滑点确实不错. 有朋友在其他平台试过么,你们用挂单的时候有没有出现过挂单滑点的呢. ...
My name is 张代理 ~
发表于:2016-06-22 08:04只看该作者
4楼
在真正的ecn平台上,挂单解决不了滑点,特别是剧烈波动时,一旦挂单不成交,你的亏损会瞬间扩大许多,结果本来市价止损的,最终导致几倍的亏损都有可能。
点评
发表于 2016-06-22 08:07
韬客社区www.talkfx.co
5楼
wsxsww 发表于 2016-6-22 16:04
在真正的ecn平台上,挂单解决不了滑点,特别是剧烈波动时,一旦挂单不成交,你的亏损会瞬间扩大许多,结果 ...
点评
发表于 2016-06-22 08:10
My name is 张代理 ~
发表于:2016-06-22 08:10只看该作者
6楼
张翠山 发表于 2016-6-22 16:07
挂单不能用于止损,挂单是开仓时候用的,止损是平仓的范畴 挂单只能是让你差一点没有买到,接下来大涨你 ...
点评
发表于 2016-06-22 08:20
韬客社区www.talkfx.co
8楼
wsxsww 发表于 2016-6-22 16:10
开仓挂单当然没问题,可能的风险就是因为几点错失一次好机会。
点评
发表于 2016-06-22 08:25
My name is 张代理 ~
发表于:2016-06-22 08:21只看该作者
9楼
学习,太高深了!
韬客社区www.talkfx.co
发表于:2016-06-22 08:24只看该作者
11楼
技术帖, 不明觉厉!
翠花, 有空能不能弄个小程序用来统计详细交易报表, 类似myfxbook显示的那些数据, 资金曲线图,赢亏比,胜率,回撤等等...造福广大dukascopy用户.
发表于:2016-06-22 08:25只看该作者
12楼
张翠山 发表于 2016-6-22 16:20
是啊,所以说各有利弊,最优价位和确保执行二选一。 不过做的周期大的,执行基本没有问题,
韬客社区www.talkfx.co
发表于:2016-06-22 08:26只看该作者
13楼
sanjiang 发表于 2016-6-22 16:23
其实最需要解决滑点问题的就是应用在止损那里的. 开单不需要考虑滑点问题的. 当前价格到了你的条件就用 ...
点评
发表于 2016-06-22 08:46
韬客社区www.talkfx.co
发表于:2016-06-22 08:35只看该作者
14楼
本帖最后由 sanjiang 于 2016-6-22 16:39 编辑
这个不成交的风险在赢透之类的交易商会存在.在对赌平台如果不存在的话就不用怕吧.我用的IC立即成交模式都从来没有出现过成交失败的,只有滑点情况.
对赌平台是鼓励交易的,那挂单交易我觉得也不会成交失败,或者一千次也难得出现一次挂单成交失败的情况.
立即交易平台容易判断方向,卖单给你低价,买单给你高价,提高你成本.
挂单交易平台不好判断方向吧,所以要问实盘中有经历过的人,看看实际情况的.
wsxsww 发表于 2016-6-22 16:26
止损是解决不了滑点的问题的,这个无解。如果用市价止损,就会滑点;如果用挂单止损,意味着不成交的风险 ...
点评
发表于 2016-06-22 08:40
韬客社区www.talkfx.co
15楼
sanjiang 发表于 2016-6-22 16:23
其实最需要解决滑点问题的就是应用在止损那里的. 开单不需要考虑滑点问题的. 当前价格到了你的条件就用 ...
My name is 张代理 ~
发表于:2016-06-22 08:40只看该作者
16楼
sanjiang 发表于 2016-6-22 16:35
这个不成交的风险在赢透之类的交易商会存在.在对赌平台如果不存在的话就不用怕吧.我用的IC立即成交模式都 ...
发表于:2016-06-22 08:41只看该作者
17楼
张翠山 发表于 2016-6-22 16:37
止损没办法,因为价格一直跌,你这个时候平仓等于执行卖的操作, 都卖的时候还想在理想价格卖出去,不成 ...
点评
发表于 2016-06-22 08:44
韬客社区www.talkfx.co
发表于:2016-06-22 08:42只看该作者
19楼
本帖最后由 wsxsww 于 2016-6-22 16:44 编辑
还有挂单只撮合部分成交在ecn平台是完全存在的,别说10手以上,有时1手都会存在这样的情况。
点评
发表于 2016-06-22 08:49
韬客社区www.talkfx.co
20楼
sanjiang 发表于 2016-6-22 16:41
去实盘研究研究才能有答案. 话说你EA今晚会关闭么,明后两天还敢开着么.