第四章:设计模式在交易系统中的应用

设计模式这东西,很多人觉得是理论派的东西。但我做了这么多年量化系统,可以负责任地说:没有设计模式,你的交易系统就是一盘散沙

今天咱们聊四个最常用的模式:单例、策略、观察者、工厂。每个模式我都踩过坑,也都有血泪教训。

4.1 单例模式:配置管理

先问个问题:你的交易系统里,配置信息应该存在几个地方?

答案很明显——一个就够了。如果配置散落在各个模块里,改一个参数得翻遍整个代码库,那画面太美我不敢看。

单例模式就是干这个的。它保证一个类只有一个实例,并且提供一个全局访问点。

核心要点:配置管理必须全局唯一,且线程安全。

我在项目中遇到过一个问题:多个线程同时读取配置,结果有的线程读到旧值,有的读到新值。嗯,这就是典型的并发问题。

来看看一个靠谱的配置管理器怎么写:

public class ConfigManager {
    // volatile 保证可见性
    private static volatile ConfigManager instance;
    private Properties config;
    
    private ConfigManager() {
        config = new Properties();
        loadConfig();
    }
    
    // 双重检查锁,既保证线程安全,又保证性能
    public static ConfigManager getInstance() {
        if (instance == null) {
            synchronized (ConfigManager.class) {
                if (instance == null) {
                    instance = new ConfigManager();
                }
            }
        }
        return instance;
    }
    
    public String get(String key) {
        return config.getProperty(key);
    }
    
    private void loadConfig() {
        // 从文件或数据库加载配置
        // 我习惯用 YAML 格式,可读性好
    }
}
小技巧:我习惯用枚举实现单例,代码更简洁,而且天然防反射攻击。

你想想看,如果每个模块都自己读配置文件,那改个数据库连接地址得改多少个地方?单例模式说白了就是统一管理,避免混乱

4.2 策略模式:交易策略切换

交易系统最核心的是什么?策略切换。

今天用均线策略,明天换动量策略,后天可能又加个统计套利。如果每次切换都改主代码,那你的系统活不过三个月。

策略模式就是解决这个问题的。它把每个策略封装成独立的类,让它们可以互相替换。

我曾经接手过一个项目,策略切换用了一堆 if-else,足足 500 行。每次加新策略都得改主流程,测试要跑三天。后来我重构成了策略模式,加新策略只需要新增一个类,主流程一行代码都不用改。

// 策略接口
public interface TradingStrategy {
    Signal evaluate(MarketData data);
}

// 具体策略:均线策略
public class MovingAverageStrategy implements TradingStrategy {
    @Override
    public Signal evaluate(MarketData data) {
        // 计算均线,生成信号
        return Signal.BUY;
    }
}

// 具体策略:动量策略
public class MomentumStrategy implements TradingStrategy {
    @Override
    public Signal evaluate(MarketData data) {
        // 计算动量,生成信号
        return Signal.SELL;
    }
}

// 策略上下文
public class StrategyContext {
    private TradingStrategy strategy;
    
    public void setStrategy(TradingStrategy strategy) {
        this.strategy = strategy;
    }
    
    public Signal execute(MarketData data) {
        return strategy.evaluate(data);
    }
}
避坑指南:我曾经犯过一个错误——策略之间共享了全局状态。结果两个策略互相影响,回测数据一塌糊涂。记住:每个策略应该是无状态的,或者状态完全隔离

说白了,策略模式就是把变化的部分抽出来,让系统更灵活。你想想看,如果哪天老板说「加个新策略」,你只需要写一个新类,是不是很爽?

4.3 观察者模式:行情推送

行情数据来了,谁需要知道?

策略模块需要、风控模块需要、日志模块需要、UI 展示也需要。如果每个模块都去轮询行情服务器,那服务器早被你搞崩了。

观察者模式就是干这个的。它定义了一种一对多的依赖关系,当一个对象状态变化时,所有依赖它的对象都会收到通知。

我记得有一次做高频交易系统,行情推送延迟了 50 毫秒,结果策略全部失效。后来我优化了观察者模式的实现,用事件总线替代了传统的观察者列表,延迟降到了 5 毫秒以内。

// 行情主题
public class MarketDataSubject {
    private List<MarketDataObserver> observers = new ArrayList<>();
    
    public void attach(MarketDataObserver observer) {
        observers.add(observer);
    }
    
    public void detach(MarketDataObserver observer) {
        observers.remove(observer);
    }
    
    public void notifyObservers(MarketData data) {
        for (MarketDataObserver observer : observers) {
            observer.update(data);
        }
    }
}

// 观察者接口
public interface MarketDataObserver {
    void update(MarketData data);
}

// 具体观察者:策略模块
public class StrategyObserver implements MarketDataObserver {
    @Override
    public void update(MarketData data) {
        // 处理行情,生成交易信号
    }
}

// 具体观察者:风控模块
public class RiskObserver implements MarketDataObserver {
    @Override
    public void update(MarketData data) {
        // 检查风险指标
    }
}
性能关键:在高频场景下,观察者通知不能阻塞。我建议用异步队列或者 Disruptor 框架,避免一个观察者拖慢整个链路。

为什么会这样?因为行情数据是实时流,你不能让任何一个消费者拖慢生产者。观察者模式用得好,系统就像流水线一样顺畅。

4.4 工厂模式:订单创建

订单创建看起来简单,其实坑很多。

市价单、限价单、止损单、冰山单……每种订单的创建逻辑都不一样。如果直接 new 对象,那你的代码里到处都是 if-else。

工厂模式就是用来封装对象创建逻辑的。它把「创建对象」和「使用对象」分离开,让代码更清晰。

我见过一个项目,订单创建逻辑散落在 10 个不同的文件里,每次改订单格式都得找半天。后来我用工厂模式重构,所有订单创建都集中在一个地方,维护成本直线下降。

// 订单接口
public interface Order {
    void execute();
}

// 具体订单:市价单
public class MarketOrder implements Order {
    @Override
    public void execute() {
        // 市价成交逻辑
    }
}

// 具体订单:限价单
public class LimitOrder implements Order {
    @Override
    public void execute() {
        // 限价成交逻辑
    }
}

// 订单工厂
public class OrderFactory {
    public static Order createOrder(String type, OrderParams params) {
        switch (type) {
            case "MARKET":
                return new MarketOrder(params);
            case "LIMIT":
                return new LimitOrder(params);
            case "STOP":
                return new StopOrder(params);
            default:
                throw new IllegalArgumentException("未知订单类型: " + type);
        }
    }
}
进阶技巧:我习惯用反射或者注册表模式来实现工厂,这样加新订单类型时不需要修改工厂代码,完全符合开闭原则。

你想想看,如果交易所新增了一种订单类型,你只需要新增一个类,然后在工厂里注册一下。主流程完全不用动,是不是很优雅?

知识体系总览

这四个模式在交易系统里是怎么配合的?我画了一张图,你看一眼就明白了:

交易系统设计模式架构图 配置管理 单例模式 行情推送 观察者模式 策略切换 策略模式 订单创建 工厂模式 读取配置 推送行情 生成信号 说明: 1. 配置管理(单例)为所有模块提供统一的配置访问 2. 行情推送(观察者)将市场数据实时分发给各消费者 3. 策略切换(策略)根据行情生成交易信号 4. 订单创建(工厂)根据信号生成具体订单

这张图展示了四个模式在交易系统中的协作关系。配置管理为所有模块提供参数,行情推送把数据分发给策略,策略生成信号后交给工厂创建订单。环环相扣,缺一不可。

总结一下

这四个模式,说白了就是解决四个核心问题:

  • 单例模式:配置统一管理,别到处乱放
  • 策略模式:策略灵活切换,别写死 if-else
  • 观察者模式:行情实时推送,别让消费者轮询
  • 工厂模式:订单统一创建,别散落各处

我在实际项目中,这四个模式几乎是标配。没有它们,系统就像没有骨架的人,站都站不起来。

嗯,今天就聊到这儿。代码质量不是一天练成的,但用好设计模式,你已经走对了第一步。

专注资料整理