65.9K
CodeProject 正在变化。 阅读更多。
Home

创建机械交易系统第二部分:四成交易系统

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.52/5 (6投票s)

2006 年 8 月 18 日

CPOL

6分钟阅读

viewsIcon

67332

downloadIcon

1773

本文介绍如何使用 C# 模拟器实现和测试机械交易系统,例如 Ned Davis 的 4% 模型。

Sample Image - Trading_System.jpg

引言

本系列第一篇文章介绍了使用 C# 语言为 .NET 平台编写机械交易系统机器人的项目。本文将介绍如何编写一个交易系统,该系统可以观察价格走势并做出买卖决策。本项目旨在编写一个不依赖任何底层平台来执行的程序,而是直接从经纪商接收价格数据并通过 API 下单。

此处提供的代码是创建回测平台和自动化 FOREX 交易机器人的更大项目的一部分。本项目的源代码可在此处获取。

背景

本文实现了由 Ned Davis 开发并由 Martin Zweig 在其著作《Winning on Wall Street》中推广的 4% 模型。这是一个非常简单的趋势跟踪模型,可以让你顺应持续的趋势。这并不是说该系统没有弱点。如果选择的百分比过小,系统可能会出现“金叉死叉”(whipsaw),即执行许多小额亏损交易。

该系统根据价格相对于第一个观察到的价格上涨或下跌的百分比来做出初始的买卖决策。一旦确定了初始方向,系统会在价格从局部最高价或最低价回撤一定百分比时反转头寸。例如,如果系统正在买入,价格上涨到 104 的最高点,然后回撤到 100,系统将平仓多头并建立空头头寸。

这是一个反转系统,并且它始终在市场中。要充分发挥其潜力,必须在交易者可以轻松地进行多头或空头交易的市场中实施。期货、期权和外汇市场都具有此特性。

本项目使用随机游走算法生成模拟价格数据。算法如下框所示。每次调用该函数时,都会抛掷一枚虚拟硬币。如果是正面,价格上涨 1 美分;如果是反面,价格下跌 1 美分。然后将此随机数据发送给账户管理器和交易系统。交易系统做出决策,如果想要买入或卖出,则通知账户管理器。然后账户管理器将交易日志输出到屏幕。

模拟交易示例

本文开头的截图显示了算法通过随机游走数据运行的示例。价格从 100 开始,并以 1 美分的幅度随机上下移动。由于 100 将是第一个价格,第一个决策将是在价格上涨 4% 到 104 时买入,或在价格下跌 4% 到 96 时卖出。在此示例中,首先达到 96 的价格目标,系统发出卖单。系统现在正在寻找新的低点,如果价格从这个新低点回撤 4%,系统将平仓卖单并发出买单。在此示例中,如图所示,系统在 98.01 处反转头寸,因为价格必须已从 98.01 下跌至 94.08(下跌 4%)。在假设的示例中,在添加屏幕上显示的交易后,系统显示了 4.92 的利润。这个非常简单的系统可以让你顺应持续的趋势,但如果百分比不适合最大值和最小值之间的摆动幅度,则容易出现“金叉死叉”。或许使用 ATR 或 ADX 指标可以减少“金叉死叉”。

Using the Code

本项目已安排好,以便从 4xlab.net 项目中获取的未修改的交易系统能够成功执行。类支持框架以最小化的方式实现,以允许该系统输出交易决策。诸如参数优化和包含损益及回撤数据的交易结束报告之类的功能在此项目中不可用。

FourPercentModel.cs 文件包含交易系统的完整实现。fPercentageChange 类继承自 ForexObject。这使得框架能够通过将交易系统视为其基类 (ForexObject) 来接收和执行它们。

基类 ForexObject 定义了以下方法

  • public override void Init(string pParameters) 

    此方法初始化对象。选择此方法而不是构造函数是为了允许现有对象重新初始化,并允许对象记住多次使用不同参数运行以进行优化。

    通过向该对象发送一个参数 string 来初始化该系统,其中参数:值对由分号分隔。如果未发送任何参数,对象将负责选择默认值。

  • public override void ReceiveTick(sTick pTick) 

    以下两个方法用于对象接收实时报价数据。如果对象希望接收 tick 数据,它会将自己注册为 tick 监听器。private 方法 DecisionFunction 必须在此处或 ReceiveCandle 方法中调用。

    DecisionFunction 方法负责分析报价数据并做出通过 Account 对象执行的买卖决策。在此示例中,可以看到决策函数涉及根据当前的交易方向寻找新的最低点或最高点。一旦价格回撤指定百分比,系统就会反转方向。通过调用 Framework.Account.EnterTrade(int pDirection) 来进入交易。对于买单,使用 +1 作为方向;对于卖单,使用 -1。Account 对象通过 Framework.Account.InTrade() 方法返回其交易状态。正值表示有买单正在进行;负值表示有卖单正在进行。如果没有开放的交易,则返回零。

  • public override void ReceiveCandle(sCandle pCandle, int pPeriod, string pCBTitle) 

    此方法用于对象接收 candle 数据。为此,对象必须将自己注册为 candle 监听器。

  • public override bool Finished() 

    此方法允许框架多次调用对象。对象可以选择在每次运行结束时更改报价数据并在内部进行比较。如果是这样,当对象第一次完成接收报价数据并调用此方法时,对象将切换报价数据并返回 false。第二次调用时,此方法将返回 true

  • public override string Title() 

    此方法允许集合中的各个对象通过一个友好的名称进行识别。

文件:FourPercentModel.cs

//
// 4X Lab.NET © Copyright 2005-2006 ASCSE LLC
// http://www.4xlab.net
// email: 4xlab@4xlab.net
//

using System;

namespace FourPercentShell
{
    // UNMODIFIED CLASS, SAME AS IN 4XLAB.NET    
    public class fPercentageChange : ForexObject
    {
    double iPercentage;
    double iMin;
    double iMax;

    bool   FirstTick;
    sTick  Tick;
    double Spread;

    public fPercentageChange()
    {
    }

    public override void Init(string pParameters)
    {
        this.InitializeParameters(pParameters,"PERCENT:0.001;");
        iPercentage = PParser.GetDouble("PERCENT",0);
        Framework.TickServer.RegisterTickListener("System","*",this);
        Framework.WriteGraphLine("InTrade,Margin,Tick,Min,MinTh,Max,MaxTh");
        FirstTick = true;
    }

    public override void ReceiveTick(sTick pTick)
    {
        Spread    = pTick.Ask - pTick.Bid;
        Tick    = pTick;

        DecisionFunction();
    }

    public override void ReceiveCandle(sCandle pCandle, int pPeriod, string pCBTitle)
    {
    }
    
    private void DecisionFunction()
    {
        if (FirstTick)
        {
        FirstTick = false;
        iMin = Tick.Bid;
        iMax = Tick.Bid;
        }
        else
        {
        if (Framework.Account.InTrade()==1)
        {
            if (Tick.Bid > iMax)
            {
            iMax = Tick.Bid;
            }
            
            if ((iMax-Tick.Bid) > iPercentage*iMax)
            {
            Framework.Account.ExitTrade();
            Framework.Account.EnterTrade(-1);
            iMin = Tick.Bid;
            iMax = 0;
            }
        }

        if (Framework.Account.InTrade()==-1)
        {
            if (Tick.Bid < iMin)
            {
            iMin = Tick.Bid;
            }
            
            if ((Tick.Bid-iMin) > iPercentage*iMin)
            {
            Framework.Account.ExitTrade();
            Framework.Account.EnterTrade(1);
            iMax = Tick.Bid;
            iMin = 0;
            }
        }

        if (Framework.Account.InTrade()==0)
        {

            if ((Tick.Bid-iMin) > iPercentage*iMin)
            {
            Framework.Account.EnterTrade(1);
            iMax = Tick.Bid;
            iMin = 0;
            }

            if ((iMax-Tick.Bid) > iPercentage*iMax)
            {
            Framework.Account.EnterTrade(-1);
            iMin = Tick.Bid;
            iMax = 0;
            }
        }
        }

        double logInTrade = Framework.Account.InTrade();
        double logMargin  = Framework.Account.GetAccount().C;

        double MinTh = iMin + iMin*iPercentage;
        double MaxTh = iMax - iMax*iPercentage;

        Framework.WriteGraphLine(logInTrade+","+logMargin+","+Tick.Bid+","+
                     iMin+","+MinTh+","+iMax+","+MaxTh);
    }

    public override bool Finished()
    {
        bool returnv = true;

        return returnv;
    }

    public override string Title()
    {
        return "% Trend Change";
    }
    }
}

以下文件显示了 Tick 服务器如何基于随机游走模型生成模拟价格数据。首先,根据随机数增加或减少价格。然后创建一个带有 Bid Ask 价格的 Tick 对象。之后,tick 首先发送到账户模拟器,然后发送到已注册的 tick 监听器。此处实现的 code 只允许在账户模拟器之外有一个已注册的 tick 监听器。

文件:Framework.cs

    public class oTickServer
    {
    
    ...

    public void GenerateTick()
    {
        int randomint = (int)(RNG.NextDouble()*1000);

        if (randomint>500)
        {
        price++;
        }
        else
        {
        price--;
        }

        Tick = new sTick();

        Tick.Bid = price;
        Tick.Ask = Tick.Bid + 0.01;

        Framework.Account.ReceiveTick(Tick);
        tl.ReceiveTick(Tick);
    }

    ...

    }

了解更多

历史

  • 2006 年 8 月 17 日:初次发布
© . All rights reserved.