Giter Site home page Giter Site logo

qtrader's Introduction

QTrader: A Light Event-Driven Algorithmic Trading Engine

Latest update on 2022-09-07

Key modifications

  • 2022-09-07: Moved qtrader.config to qtrader_config

Introduction

QTrader is a light and flexible event-driven algorithmic trading engine that can be used to backtest strategies, and seamlessly switch to live trading without any pain.

Key Features

  • Completely same code for backtesting / simulation / live trading

  • Support trading of various assets: equity, futures

  • Resourceful functionalities to support live monitoring and analysis

Quick Install

You may run the folllowing command to install QTrader immediately:

# Virtual environment is recommended (python 3.8 or above is supported)
>> conda create -n qtrader python=3.8
>> conda activate qtrader

# Install stable version from pip (currently version 0.0.4)
>> pip install qtrader

# Alternatively, install latest version from github 
>> pip install git+https://github.com/josephchenhk/qtrader@master

Create a Configuration File

At your current working directory, create qtrader_config.py if not exists. There is an example qtrader_config_sample.py for your reference. Adjust the items if necessary.

Prepare the Data

QTrader supports bar data at the moment. What you need to do is creating a folder with the name of the security you are interested in. Let's say you want to backtest or trade HK equity "HK.01157" in frequency of 1 minute, your data folder should be like this (where "K_1M" stands for 1 minute; you can also find a sample from the qtrader/examples/data):

alt text

And you can prepare OHLCV data in CSV format, with dates as their file names, e.g., "yyyy-mm-dd.csv":

alt text

Inside each csv file, the data columns should look like this:

alt text

Now you can specify the path of data folder in qtrader/config/config.py qtrader_config.py. For example, set

DATA_PATH = {
    "kline": "path_to_your_qtrader_folder/examples/data/k_line",
}

Implement a Strategy

To implement a strategy is simple in QTrader. A strategy needs to implement init_strategy and on_bar methods in BaseStrategy. Here is a quick sample:

from qtrader.core.strategy import BaseStrategy

class MyStrategy(BaseStrategy):

    def init_strategy(self):
        pass
        
    def on_bar(self, cur_data:Dict[str, Dict[Security, Bar]]):
        print(cur_data)

Record Variables

QTrader provides a module named BarEventEngineRecorder to record variables during backtesting and/or trading. By default it saves datetime, portfolio_value and action at every time step.

If you want to record additional variables (let's say it is called var), you need to write a method called get_var in your strategy:

from qtrader.core.strategy import BaseStrategy

class MyStrategy(BaseStrategy):

    def get_var(self):
        return var

And initialize your BarEventEngineRecorder with the same vairable var=[](if you want to record every timestep) or var=None(if you want to record only the last updated value):

recorder = BarEventEngineRecorder(var=[])

Run a Backtest

Now we are ready to run a backtest. Here is a sample of running a backtest in QTrader:

# Security 
stock_list = [
    Stock(code="HK.01157", lot_size=100, security_name="中联重科", exchange=Exchange.SEHK),
]

# Gateway
gateway_name = "Backtest"
gateway = BacktestGateway(
    securities=stock_list,
    start=datetime(2021, 3, 15, 9, 30, 0, 0),
    end=datetime(2021, 3, 17, 16, 0, 0, 0),
    gateway_name=gateway_name,
)
gateway.SHORT_INTEREST_RATE = 0.0
gateway.set_trade_mode(TradeMode.BACKTEST)

# Core engine
engine = Engine(gateways={gateway_name: gateway})

# Strategy initialization
init_capital = 100000
strategy_account = "DemoStrategy"
strategy_version = "1.0"
strategy = DemoStrategy(
    securities={gateway_name: stock_list},
    strategy_account=strategy_account,
    strategy_version=strategy_version,
    init_strategy_cash={gateway_name: init_capital},
    engine=engine,
    strategy_trading_sessions={
        "HK.01157": [
            [datetime(1970, 1, 1, 9, 30, 0), datetime(1970, 1, 1, 12, 0, 0)],
            [datetime(1970, 1, 1, 13, 0, 0), datetime(1970, 1, 1, 16, 0, 0)],
        ],
)
strategy.init_strategy()

# Recorder
recorder = BarEventEngineRecorder()

# Event engine
event_engine = BarEventEngine(
    {"demo": strategy},
    {"demo": recorder},
    engine
)

# Start event engine
event_engine.run()

# Program terminates normally
engine.log.info("Program shutdown normally.")

After shutdown, you will be able to find the results in qtrader/results, with the folder name of latest time stamp:

alt text

The result.csv file saves everything you want to record in BarEventEngineRecorder; while pnl.html is an interactive plot of the equity curve of your running strategy:

alt text

Simulation / Live trading

Ok, your strategy looks good now. How can you put it to paper trading and/or live trading? In QTrader it is extremely easy to switch from backtest mode to simulation or live trading mode. What you need to modify is just two lines (replace a backtest gateway with a live trading gateway!):

# Currently you can use "Futu", "Ib", and "Cqg" 
gateway_name = "Futu"  

# Use FutuGateway, IbGateway, or CqgGateway accordingly
# End time should be set to a future time stamp when you expect the program terminates
gateway = FutuGateway(
    securities=stock_list,
    end=datetime(2022, 12, 31, 16, 0, 0, 0),  
    gateway_name=gateway_name,
)

# Choose either TradeMode.SIMULATE or TradeMode.LIVETRADE
gateway.set_trade_mode(TradeMode.LIVETRADE)

That's it! You switch from backtest to simulation / live trading mode now.

Important Notice: In the demo sample, the live trading mode will keep on sending orders, please be aware of the risk when running it.

Live Monitoring

When running the strategies, the trader typically needs to monitor the market and see whether the signals are triggered as expected. QTrader provides with such dashboard(visualization panel) which can dynamically update the market data and gives out entry and exit signals in line with the strategies.

You can activate this function in your qtrader_config.py:

ACTIVATED_PLUGINS = [.., "monitor"]

After running the main script, you will be able to open a web-based monitor in the browser: 127.0.0.1:8050:

alt text

QTrader is also equipped with a Telegram Bot, which allows you get instant information from your trading program. To enable this function, you can add your telegram information in qtrader_config.py(you can refer to the following link for detailed guidance):

ACTIVATED_PLUGINS = [.., "telegram"]

TELEGRAM_TOKEN = "50XXXXXX16:AAGan6nFgmrSOx9vJipwmXXXXXXXXXXXM3E"
TELEGRAM_CHAT_ID = 21XXXXXX49

In this way, your mobile phone with telegram will automatically receive a documenting message:

You can use your mobile phone to monitor and control your strategy now.

Contributing

  • Fork it (https://github.com/josephchenhk/qtrader/fork)
  • Study how it's implemented.
  • Create your feature branch (git checkout -b my-new-feature).
  • Use flake8 to ensure your code format complies with PEP8.
  • Commit your changes (git commit -am 'Add some feature').
  • Push to the branch (git push origin my-new-feature).
  • Create a new Pull Request.

qtrader's People

Contributors

josephchenhk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qtrader's Issues

from qtrader.core.event_engine import BarEventEngineRecorder, BarEventEngine error

Traceback (most recent call last):
File "E:/python/yovole/ai-project/stock/qtrader-master/examples/demo_strategy/qunater.py", line 3, in
from qtrader.core.event_engine import BarEventEngineRecorder, BarEventEngine
File "event_engine.py", line 30, in init qtrader.core.event_engine
File "engine.py", line 37, in init qtrader.core.engine
File "C:\Users\yangzhipeng\AppData\Local\Programs\Python\Python38\lib\site-packages\qtrader\core\portfolio.py", line 22, in
from qtrader.gateways import BaseGateway
File "C:\Users\yangzhipeng\AppData\Local\Programs\Python\Python38\lib\site-packages\qtrader\gateways_init_.py", line 32, in
from .cqg import CqgGateway
File "C:\Users\yangzhipeng\AppData\Local\Programs\Python\Python38\lib\site-packages\qtrader\gateways\cqg_init_.py", line 18, in
from .cqg_gateway import CqgGateway
File "C:\Users\yangzhipeng\AppData\Local\Programs\Python\Python38\lib\site-packages\qtrader\gateways\cqg\cqg_gateway.py", line 23, in
from qtrader.gateways.cqg.wrapper.cqg_api import CQGAPI
File "C:\Users\yangzhipeng\AppData\Local\Programs\Python\Python38\lib\site-packages\qtrader\gateways\cqg\wrapper\cqg_api.py", line 33, in
from qtrader.gateways.cqg.wrapper.CELEnvironment import CELSinkBase
File "C:\Users\yangzhipeng\AppData\Local\Programs\Python\Python38\lib\site-packages\qtrader\gateways\cqg\wrapper\CELEnvironment.py", line 10, in
win32com.client.gencache.EnsureModule('{51F35562-AEB4-4AB3-99E8-AC9666344B64}', 0, 4, 0)
File "C:\Users\yangzhipeng\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\client\gencache.py", line 598, in EnsureModule
module = MakeModuleForTypelib(
File "C:\Users\yangzhipeng\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\client\gencache.py", line 315, in MakeModuleForTypelib
makepy.GenerateFromTypeLibSpec(
File "C:\Users\yangzhipeng\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\client\makepy.py", line 257, in GenerateFromTypeLibSpec
tlb = pythoncom.LoadRegTypeLib(typelibCLSID, major, minor, lcid)
pywintypes.com_error: (-2147319779, '库没有注册。', None, None)

one question

When I try to run your demo, I got the error "No module named 'futu'". I find that futu_gateway.py import futu file, which is the file contains futu_gateway.py. Is there any problem of this usage? Or how can I fix this error? Thanks.

关于Portfolio里做空和做多的account_balance.cash计算疑惑

代码如下:

    def update(self, deal: Deal):
        security = deal.security
        lot_size = security.lot_size
        price = deal.filled_avg_price
        quantity = deal.filled_quantity
        direction = deal.direction
        offset = deal.offset
        filled_time = deal.updated_time
        fee = self.market.fees(deal).total_fees
        # update balance
        self.account_balance.cash -= fee
        if direction == Direction.LONG:
            self.account_balance.cash -= price * quantity * lot_size
            if offset == Offset.CLOSE:  # pay interest when closing short 
                short_position = self.position.holdings[security][Direction.SHORT]
                short_interest = (
                        short_position.holding_price
                        * short_position.quantity
                        * (filled_time - short_position.update_time).days / 365
                        * self.market.SHORT_INTEREST_RATE
                )
                self.account_balance.cash -= short_interest
        elif direction == Direction.SHORT:
            self.account_balance.cash += price * quantity * lot_size

1.股票:
direction=LONG开仓下是cash会减少,direction=SHORT卖出account_balance.cash余额增加,这是没有问题的。
2.期货:
direction=LONG下如果offset=CLOSE为什么cash也用减法并且还有一个account_balance.cash -= short_interest。
direction=SHORT下如果offset=OPEN为什么cash还用加法呢(self.account_balance.cash += price * quantity * lot_size)。

问题是股票我能理解,买入和卖出的金额,但是期货的做多和做空,金额的变化不太理解。希望作者能帮忙解惑期权期货Portfolio里的update逻辑怎么解释。

关于action记录的一些疑问

1、想请教一下,action的record有demo可以提供一下嘛?具体需要记录哪些字段?现在我可以用update_action更新和记录action,但是在后续一些调用上会出现利用action信息进行统计,会出现字段不匹配的问题。
2、PerformanceCTA中action['no']字段具体该填哪些信息?
if action["offset"] == "OPEN":
open_trades[gateway_name][security][action['no']] = action
elif action["offset"] == "CLOSE":
close_trd = action

Indicators and slippage

Hey!

i love that this supports CQG, have you found any way to include indicators? Perhaps using a TA python library and just injecting the bar data in? Same question for slippage, perhaps just a mathematical tweak upon entry…

Thanks

两个建议,1开源core,2去掉第三方强制依赖

你好,最近正在想找一个合适的开源回测系统,看到这个项目,试跑了下,发现几个问题:

1 没有完全开源,这样别人就不敢使用这样项目,有了问题无法解决
2 跑demo配置不是很方便,我的是Linux系统,只想跑下回测,为什么没有还需要我安装tutu,ibapi,win32com等等?
3 DATA_FFILL又是什么 ?

<class 'ModuleNotFoundError'>: No module named 'futu'
<class 'ImportError'>: cannot import name 'FutuGateway' from 'qtrader.gateways.futu' (/home/terry/Documents/demos/qtrader/qtrader/gateways/futu/__init__.py)
<class 'ModuleNotFoundError'>: No module named 'ibapi'
<class 'ImportError'>: cannot import name 'IbGateway' from 'qtrader.gateways.ib' (/home/terry/Documents/demos/qtrader/qtrader/gateways/ib/__init__.py)
<class 'ModuleNotFoundError'>: No module named 'win32com'
<class 'ImportError'>: cannot import name 'CqgGateway' from 'qtrader.gateways.cqg' (/home/terry/Documents/demos/qtrader/qtrader/gateways/cqg/__init__.py)
Traceback (most recent call last):
  File "main_demo.py", line 24, in <module>
    from qtrader.core.event_engine import BarEventEngineRecorder, BarEventEngine
  File "event_engine.py", line 35, in init qtrader.qtrader.core.event_engine
ImportError: cannot import name DATA_FFILL

mac环境修改了main_demo还是跑不起来,是不是缺少了哪个步骤

Duplicate key in file PosixPath('/Users/ZhaoJunfeng/miniconda3/envs/zjfenv/lib/python3.10/site-packages/matplotlib/mpl-data/matplotlibrc'), line 406 ('axes.unicode_minus: True # use Unicode for the minus symbol rather than hyphen. See')
Traceback (most recent call last):
File "/Users/ZhaoJunfeng/workspace/python/qtrader/examples/demo_strategy/main_demo.py", line 27, in
from qtrader.core.event_engine import BarEventEngineRecorder, BarEventEngine
ModuleNotFoundError: No module named 'qtrader.core.event_engine'

DemoStrategy的init_strategy_cash参数是不是有问题,报错了。

class DemoStrategy(BaseStrategy):
"""Demo strategy"""

def __init__(self,
             securities: Dict[str, List[Stock]],
             strategy_account: str,
             strategy_version: str,
             init_strategy_cash: Dict[str, float],
             engine: Engine,
             **kwargs
             ):
    super().__init__(
        securities=securities,
        strategy_account=strategy_account,
        strategy_version=strategy_version,
        engine=engine,
        init_strategy_cash=init_strategy_cash,
        **kwargs
    )

这个参数init_strategy_cash在 super().__init__报错了,在父类中参数换了麻烦作者改下。

插件analysis的plot_pnl_with_category方法有示例数据吗

记录添加如下参数,包括了action
recorder = BarEventEngineRecorder(datetime=[],
action=[],
open=[],
high=[],
low=[],
close=[],
volume=[])
但是生成的action数据还是解析失败。
生成的csv记录如下:
datetime,portfolio_value,action,close,high,low,open,strategy_portfolio_value,volume
2020-05-14,99950.01994,"{'side': 'LONG', 'offset': 'OPEN', 'sec': 'PDD'}|",[60.84],[61.0],[57.35],[58.38],99950.01994,[6802171]
但是action列的数据好像不对。
调用这个方法生成的。
self.update_action
麻烦大哥帮忙解答一下

Make instrument_cfg configurable in current directory

It is not flexible if we have to modify instrument_cfg.yaml file each time we want to add new instruments. Therefore it is more reasonable to put a yaml file in current working directory to overwrite the default one in qtrader.gateways folder.

Typo sharp

In analysis.metrics: sharp ratio should be sharpe ratio

engine的portfolios和strategy里的portfolios两者是有什么区别和联系呢

BaseStrategy类下看到如下几个方法:

    def get_portfolio_value(self, gateway_name: str) -> float:
        return self.engine.portfolios[gateway_name].value

    def get_strategy_portfolio_value(self, gateway_name: str) -> float:
        return self.portfolios[gateway_name].value

    def get_account_balance(self, gateway_name: str) -> AccountBalance:
        return self.engine.portfolios[gateway_name].account_balance

    def get_strategy_account_balance(self, gateway_name: str) -> AccountBalance:
        return self.portfolios[gateway_name].account_balance

    def get_position(self, gateway_name: str) -> Position:
        return self.engine.portfolios[gateway_name].position

    def get_strategy_position(self, gateway_name: str) -> Position:
        return self.portfolios[gateway_name].position

不太理解,portfolios分别在engine里获取和策略里获取,是因为engine里的投资组合不同吗,在什么场景下使用呢,希望作者能帮忙解答

No module named 'func_timeout'

Hi Joseph, thank you very much for sharing this repo.

While trying to run the main_demo.py, I ran into an error while importing the BarEventEngineRecorder, BarEventEngine from qtrader.core.event_engine. It says 'No module named func_timeout'. Any idea of a potential reason?

many thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.