第四章:设计模式在交易系统中的应用
设计模式这东西,很多人觉得是理论派的东西。但我做了这么多年量化系统,可以负责任地说:没有设计模式,你的交易系统就是一盘散沙。
今天咱们聊四个最常用的模式:单例、策略、观察者、工厂。每个模式我都踩过坑,也都有血泪教训。
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) {
// 检查风险指标
}
}
为什么会这样?因为行情数据是实时流,你不能让任何一个消费者拖慢生产者。观察者模式用得好,系统就像流水线一样顺畅。
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);
}
}
}
你想想看,如果交易所新增了一种订单类型,你只需要新增一个类,然后在工厂里注册一下。主流程完全不用动,是不是很优雅?
知识体系总览
这四个模式在交易系统里是怎么配合的?我画了一张图,你看一眼就明白了:
这张图展示了四个模式在交易系统中的协作关系。配置管理为所有模块提供参数,行情推送把数据分发给策略,策略生成信号后交给工厂创建订单。环环相扣,缺一不可。
总结一下
这四个模式,说白了就是解决四个核心问题:
- 单例模式:配置统一管理,别到处乱放
- 策略模式:策略灵活切换,别写死 if-else
- 观察者模式:行情实时推送,别让消费者轮询
- 工厂模式:订单统一创建,别散落各处
我在实际项目中,这四个模式几乎是标配。没有它们,系统就像没有骨架的人,站都站不起来。
嗯,今天就聊到这儿。代码质量不是一天练成的,但用好设计模式,你已经走对了第一步。