# ltpp **Repository Path**: phil_tiny/ltpp ## Basic Information - **Project Name**: ltpp - **Description**: 基于lightning-trader的cpp框架 - **Primary Language**: C++ - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 4 - **Created**: 2023-08-08 - **Last Updated**: 2023-08-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # ltpp #### 介绍 LTPP基于lightning-trader的cpp框架,主要为提供期货高频交易,对[lightning-trader](https://gitee.com/pursue-wealth/lightning-trader)的api进行了封装 - QQ交流群:980550304 - 开发者QQ:137336521 #### 使用说明 1. **创建一个信号** 创建信号需要继承lt::signal并且实现public lt::tick_receiver或者bar_receiver ``` #pragma once #include #include "receiver.h" #include class tick_signal : public lt::signal, public lt::tick_receiver { public: tick_signal(lt::sigid_t id, lt::engine& engine,const lt::param& p): signal(id,engine), _code(p.get("code")) { }; ~tick_signal(){}; public: /* * 初始化事件 * 生命周期中只会回调一次 */ virtual void on_init(lt::subscriber& suber) override; /* * 销毁 */ virtual void on_destory(lt::unsubscriber& unsuber) override; /* * tick推送 */ virtual void on_tick(const tick_info& tick, const deal_info& deal) override; private: code_t _code ; }; ``` on_init里面订阅行情,这里可以订阅tick数据,也可以订阅bar数据,on_destory里面取消订阅 ``` #include "engine.h" #include "tick_signal.h" void tick_signal::on_init(lt::subscriber& suber) { suber.regist_tick_receiver(_code,this); }; void tick_signal::on_destory(lt::unsubscriber& unsuber) { unsuber.unregist_tick_receiver(_code,this); }; void tick_signal::on_tick(const tick_info& tick, const deal_info& deal) { if(tick.price == tick.buy_price()) { trigger(tick.id, lt::ACT_BULL, tick.price); } else if(tick.price == tick.sell_price()) { trigger(tick.id, lt::ACT_BEAR, tick.price); } } ``` factory的make_signal里面注册signal ``` std::shared_ptr make_signal(lt::engine& eng, const signal_info& sig_info) { std::shared_ptr result; LOG_INFO("make_strategys : %d %d %s", sig_info.id, sig_info.type, sig_info.param.c_str()); const auto& p = lt::param(sig_info.param.c_str()); switch (static_cast(sig_info.type)) { case signal_type::SGN_TICK: result = std::make_shared(sig_info.id, eng, p); break; } return result; } ``` 2. **创建一个策略** 创建策略和创建信号类似,需要public lt::strategy并且实现lt::signal_handler ``` #pragma once #include "strategy.h" #include "receiver.h" #include class ssg_1_strategy : public lt::strategy,public lt::signal_handler { struct persist_data { uint32_t trading_day; estid_t order_estid; persist_data() : trading_day(0x0U), order_estid(INVALID_ESTID) { } }; public: ssg_1_strategy(lt::straid_t id, lt::engine& engine, bool openable, bool closeable, const lt::param& p): strategy(id, engine, openable, closeable), _signal(p.get("signal")), _open_once(p.get("open_once")), _position_limit(p.get("position_limit")), _cancel_second(p.get("cancel_second")), _coming_to_close(0), _order_data(nullptr) {}; ~ssg_1_strategy(){}; public: /* * 初始化事件 * 生命周期中只会回调一次 */ virtual void on_init() override ; /* * 交易日初始化完成 */ virtual void on_ready() override; /* * 信号处理器 */ virtual void on_signal(lt::sigid_t sigid, const code_t& code, lt::action_type type, double_t price) override; /* * 订单接收回报 * @is_success 是否成功 * @order 本地订单 */ virtual void on_entrust(const order_info& order) override; /* * 成交回报 * * @localid 本地订单id */ virtual void on_trade(estid_t localid, const code_t& code, offset_type offset, direction_type direction, double_t price, uint32_t volume) override; /* * 撤单 * @localid 本地订单id */ virtual void on_cancel(estid_t localid, const code_t& code, offset_type offset, direction_type directionv, double_t price, uint32_t cancel_volume, uint32_t total_volume) override; /* * 错误 * @localid 本地订单id * @error 错误代码 */ virtual void on_error(error_type type,estid_t localid, const uint32_t error) override; /* * 销毁 */ virtual void on_destory()override; private: lt::sigid_t _signal; uint32_t _open_once ; uint32_t _position_limit ; time_t _coming_to_close; persist_data* _order_data; uint32_t _cancel_second; }; ``` 简单的策略逻辑的实现 ``` #include "ssg_1_strategy.h" #include "time_utils.hpp" using namespace lt; void ssg_1_strategy::on_init() { subscrib_signal(_signal, this); use_custom_chain(true); _order_data = static_cast(get_userdata(sizeof(persist_data))); } void ssg_1_strategy::on_ready() { uint32_t trading_day = get_trading_day(); _coming_to_close = make_datetime(trading_day, "14:58:00"); if(_order_data->trading_day!=trading_day) { _order_data->trading_day = trading_day; _order_data->order_estid = INVALID_ESTID; } else { auto& order = get_order(_order_data->order_estid); if (order.est_id != INVALID_ESTID) { set_cancel_condition(order.est_id, [this,&order](const tick_info& tick)->bool { if (tick.time - order.create_time > _cancel_second) { return true; } if (tick.time > _coming_to_close) { return true; } return false; }); } else { _order_data->order_estid = INVALID_ESTID; } } } void ssg_1_strategy::on_signal(lt::sigid_t sigid, const code_t& code, lt::action_type type, double_t price) { LOG_INFO("ssg_1_strategy on_signal %s %d \n", code.get_id(), type); if (!is_trading_ready()) { LOG_DEBUG("ssg_1_strategy is_trading_ready not ready %s\n", code.get_id()); return; } if (get_last_time() > _coming_to_close) { //LOG_DEBUG("sig_1_strategy time > _coming_to_close %s %d %d\n", tick.id.get_id(), tick.time, _coming_to_close); return; } if (_order_data->order_estid != INVALID_ESTID) { return; } auto& tick = get_last_tick(code); if(tick.invalid()) { return; } const auto& pos = get_position(code); if (type == ACT_BEAR) { if (pos.yestoday_long.usable() > 0) { uint32_t volume = std::min(_open_once, pos.yestoday_long.usable()); //卖出 _order_data->order_estid = sell_for_close(code, volume, tick.sell_price()); } else if (pos.today_long.usable()) { uint32_t volume = std::min(_open_once, pos.today_long.usable()); //卖出 _order_data->order_estid = sell_for_close(code, volume, tick.sell_price()); } else { uint32_t volume = std::min(_open_once, _position_limit - pos.get_short_position() - get_open_short_pending(code)); if (volume > 0) { _order_data->order_estid = sell_for_open(code, volume, tick.sell_price()); } } } else if (type == ACT_BULL) { if (pos.yestoday_short.usable() > 0) { //买入 uint32_t volume = std::min(_open_once, pos.yestoday_short.usable()); _order_data->order_estid = buy_for_close(code, volume, tick.buy_price()); } else if (pos.today_short.usable()) { //买入 uint32_t volume = std::min(_open_once, pos.today_short.usable()); _order_data->order_estid = buy_for_close(code, volume, tick.buy_price()); } else { uint32_t volume = std::min(_open_once, _position_limit - pos.get_long_position() - get_open_long_pending(code)); if(volume > 0) { _order_data->order_estid = buy_for_open(code, volume, tick.buy_price()); } } } } void ssg_1_strategy::on_entrust(const order_info& order) { LOG_INFO("ssg_1_strategy on_entrust : %llu %s %d %d %f %d/%d\n", order.est_id, order.code,order.direction,order.offset,order.price, order.last_volume,order.total_volume); if (_order_data->order_estid == order.est_id) { set_cancel_condition(order.est_id, [this,&order](const tick_info& tick)->bool { if(tick.time - order.create_time > _cancel_second) { return true; } if (tick.time > _coming_to_close) { return true; } return false; }); } } void ssg_1_strategy::on_trade(estid_t localid, const code_t& code, offset_type offset, direction_type direction, double_t price, uint32_t volume) { LOG_INFO("ssg_1_strategy on_trade : %llu %s %d %d %f %d\n", localid, code, direction, offset, price, volume); if (localid == _order_data->order_estid) { _order_data->order_estid = INVALID_ESTID; } } void ssg_1_strategy::on_cancel(estid_t localid, const code_t& code, offset_type offset, direction_type direction, double_t price, uint32_t cancel_volume,uint32_t total_volume) { LOG_INFO("ssg_1_strategy on_cancel : %llu %s %d %d %f %d\n", localid, code, direction, offset, price, cancel_volume); if (localid == _order_data->order_estid) { _order_data->order_estid = INVALID_ESTID; } } void ssg_1_strategy::on_error(error_type type, estid_t localid, const uint32_t error) { LOG_ERROR("ssg_1_strategy on_error : %llu %d \n", localid, error); if(type != error_type::ET_PLACE_ORDER) { return ; } if (localid == _order_data->order_estid) { _order_data->order_estid = INVALID_ESTID; } } void ssg_1_strategy::on_destory() { unsubscrib_signal(_signal, this); } ``` 在factory.cpp里面注册策略 ``` std::shared_ptr make_strategy(lt::engine& eng, const strategy_info& stra_info) { std::shared_ptr result; LOG_INFO("make_strategys : %d %d %s", stra_info.id, stra_info.type, stra_info.param.c_str()); const auto& p = lt::param(stra_info.param.c_str()); switch (static_cast(stra_info.type)) { case strategy_type::STG_SSG_1: result = std::make_shared(stra_info.id, eng, stra_info.openable, stra_info.closeable, p); break; } return result; } ``` 3. **通过配置策略绑定配置** 创建一个配置文件strategy_10w.xml来配置策略 ``` ``` 4. **回测策略** 回测数据需要自己准备期货的tick数据,格式参考bin/data,增加配置文件trading_days.xml配置要回测的交易日 ``` 20220801 20220802 20220803 20220804 20220805 20220808 20220809 20220810 20220811 20220812 20220815 20220816 20220817 20220818 20220819 20220822 20220823 20220824 20220825 20220826 20220829 20220830 20220831 ``` 运行"gallop.exe evaluate evaluate.ini strategy_10w.xml trading_days.xml"等待执行完成,在light_test可以找到回测数据 5. **实盘** 实盘需要联系期货公司开通程序化交易,获取服务器地址和authcode等信息,修改runtime.ini文件 启动"gallop.exe runtime runtime.ini strategy_10w.xml" #### 更多学习资料 - [Lightning Trader架构设计](https://zhuanlan.zhihu.com/p/622262304) - [高频交易中如何处理低延时](https://zhuanlan.zhihu.com/p/622293141) - [多线程程序设计的两种架构](https://zhuanlan.zhihu.com/p/622423099) #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request #### 特技 1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md 2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) 3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) 6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)