[![](https://travis-ci.org/kaelzhang/stock-pandas.svg?branch=master)](https://travis-ci.org/kaelzhang/stock-pandas)
[![](https://codecov.io/gh/kaelzhang/stock-pandas/branch/master/graph/badge.svg)](https://codecov.io/gh/kaelzhang/stock-pandas)
[![](https://img.shields.io/pypi/v/stock-pandas.svg)](https://pypi.org/project/stock-pandas/)
[![](https://img.shields.io/pypi/l/stock-pandas.svg)](https://github.com/kaelzhang/stock-pandas)
[ma]: #ma-simple-moving-averages
[ema]: #ema-exponential-moving-average
[macd]: #macd-moving-average-convergence-divergence
[boll]: #boll-bollinger-bands
[rsv]: #rsv-raw-stochastic-value
[kdj]: #kdj-a-variety-of-stochastic-oscillator
[kdj]: #kdjc-another-variety-of-stochastic-oscillator
[rsi]: #rsi-relative-strength-index
[bbi]: #bbi-bull-and-bear-index
[llv]: #llv-lowest-of-low-values
[hhv]: #hhv-highest-of-high-values
[column]: #column
[increase]: #increase
[style]: #style
[repeat]: #repeat
[change]: #change
[cumulation]: #cumulation-and-datetimeindex
[datetimeindex]: https://pandas.pydata.org/docs/reference/api/pandas.DatetimeIndex.html
# [stock-pandas](https://github.com/kaelzhang/stock-pandas)
**stock-pandas** inherits and extends `pandas.DataFrame` to support:
- Stock Statistics
- Stock Indicators, including:
- Trend-following momentum indicators, such as [**MA**][ma], [**EMA**][ema], [**MACD**][macd], [**BBI**][bbi]
- Dynamic support and resistance indicators, such as [**BOLL**][boll]
- Over-bought / over-sold indicators, such as [**KDJ**][kdj], [**RSI**][rsi]
- Other indicators, such as [**LLV**][llv], [**HHV**][hhv]
- For more indicators, welcome to [request a proposal](https://github.com/kaelzhang/stock-pandas/issues/new?assignees=&labels=feature&template=FEATURE_REQUEST.md&title=), or fork and send me a pull request, or extend stock-pandas yourself. You might read the [Advanced Sections](https://github.com/kaelzhang/stock-pandas#advanced-sections) below.
- To [cumulate][cumulation] kline data based on a given time frame, so that it could easily handle real-time data updates.
`stock-pandas` makes automatical trading much easier. `stock-pandas` requires Python >= **3.6** and Pandas >= **1.0.0**(for now)
With the help of `stock-pandas` and mplfinance, we could easily draw something like:
![](boll.png)
The code example is available at [here](https://github.com/kaelzhang/stock-pandas-examples/blob/master/example/bollinger_bands.ipynb).
## Install
For now, before installing `stock-pandas` in your environment
#### Have `g++` compiler installed
```sh
# With yum, for CentOS, Amazon Linux, etc
yum install gcc-c++
# With apt-get, for Ubuntu
apt-get install g++
# For macOS, install XCode commandline tools
xcode-select --install
```
If you use docker with `Dockerfile` and use python image,
```Dockerfile
FROM python:3.8
...
```
The default `python:3.8` image already contains g++, so we do not install g++ additionally.
#### Install `stock-pandas`
```sh
# Installing `stock-pandas` requires `numpy` to be installed first
pip install numpy
pip install stock-pandas
```
Be careful, you still need to install `numpy` explicitly even if `numpy` and `stock-pandas` both are contained in `requirement.txt`
```txt
numpy
stock-pandas
other-dependencies
...
```
```sh
pip install numpy
pip install -r requirement.txt
```
## Usage
```py
from stock_pandas import StockDataFrame
# or
import stock_pandas as spd
```
We also have some examples with annotations in the [`example`](https://github.com/kaelzhang/stock-pandas/tree/master/example) directory, you could use [JupyterLab](https://jupyter.org/) or Jupyter notebook to play with them.
### StockDataFrame
`StockDataFrame` inherits from `pandas.DataFrame`, so if you are familiar with `pandas.DataFrame`, you are already ready to use `stock-pandas`
```py
import pandas as pd
stock = StockDataFrame(pd.read_csv('stock.csv'))
```
As we know, we could use `[]`, which called **pandas indexing** (a.k.a. `__getitem__` in python) to select out lower-dimensional slices. In addition to indexing with `colname` (column name of the `DataFrame`), we could also do indexing by `directive`s.
```py
stock[directive] # Gets a pandas.Series
stock[[directive0, directive1]] # Gets a StockDataFrame
```
We have an example to show the most basic indexing using `[directive]`
```py
stock = StockDataFrame({
'open' : ...,
'high' : ...,
'low' : ...,
'close': [5, 6, 7, 8, 9]
})
stock['ma:2']
# 0 NaN
# 1 5.5
# 2 6.5
# 3 7.5
# 4 8.5
# Name: ma:2,close, dtype: float64
```
Which prints the 2-period simple moving average on column `"close"`.
#### Parameters
- **date_col** `Optional[str] = None` If set, then the column named `date_col` will convert and set as [`DateTimeIndex`](datetimeindex) of the data frame
- **to_datetime_kwargs** `dict = {}` the keyworded arguments to be passed to `pandas.to_datetime()`. It only takes effect if `date_col` is specified.
- **time_frame** `str | TimeFrame | None = None` time frame of the stock. For now, only the following time frames are supported:
- `'1m'` or `TimeFrame.M1`
- `'3m'` or `TimeFrame.M3`
- `'5m'` or `TimeFrame.M5`
- `'15m'` or `TimeFrame.M15`
- `'30m'` or `TimeFrame.M30`
- `'1h'` or `TimeFrame.H1`
- `'2h'` or `TimeFrame.H2`
- `'4h'` or `TimeFrame.H4`
- `'6h'` or `TimeFrame.H6`
- `'8h'` or `TimeFrame.H8`
- `'12h'` or `TimeFrame.H12`
### stock.exec(directive: str, create_column: bool=False) -> np.ndarray
Executes the given directive and returns a numpy ndarray according to the directive.
```py
stock['ma:5'] # returns a Series
stock.exec('ma:5', create_column=True) # returns a numpy ndarray
```
```py
# This will only calculate without creating a new column in the dataframe
stock.exec('ma:20')
```
The difference between `stock[directive]` and `stock.exec(directive)` is that
- the former will create a new column for the result of `directive` as a cache for later use, while `stock.exec(directive)` does not unless we pass the parameter `create_column` as `True`
- the former one accepts other pandas indexing targets, while `stock.exec(directive)` only accepts a valid **stock-pandas** directive string
- the former one returns a `pandas.Series` or `StockDataFrame` object while the latter one returns an [`np.ndarray`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html)
### stock.alias(alias: str, name: str) -> None
Defines column alias or directive alias
- **alias** `str` the alias name
- **name** `str` the name of an existing column or the directive string
```py
# Some plot library such as `mplfinance` requires a column named capitalized `Open`,
# but it is ok, we could create an alias.
stock.alias('Open', 'open')
stock.alias('buy_point', 'kdj.j < 0')
```
### stock.get_column(key: str) -> pd.Series
Directly gets the column value by `key`, returns a pandas `Series`.
If the given `key` is an alias name, it will return the value of corresponding original column.
If the column is not found, a `KeyError` will be raised.
```py
stock = StockDataFrame({
'open' : ...,
'high' : ...,
'low' : ...,
'close': [5, 6, 7, 8, 9]
})
stock.get_column('close')
# 0 5
# 1 6
# 2 7
# 3 8
# 4 9
# Name: close, dtype: float64
```
```py
try:
stock.get_column('Close')
except KeyError as e:
print(e)
# KeyError: column "Close" not found
stock.alias('Close', 'close')
stock.get_column('Close')
# The same as `stock.get_column('close')`
```
### stock.append(other, *args, **kwargs) -> StockDataFrame
Appends rows of `other` to the end of caller, returning a new object.
This method has nearly the same hehavior of [`pandas.DataFrame.append()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.append.html), but instead it returns an instance of `StockDataFrame`, and it applies `date_col` to the newly-appended row(s) if possible.
### stock.directive_stringify(directive: str) -> str
> Since 0.26.0
Gets the full name of the `directive` which is also the actual column name of the data frame
```py
stock.directive_stringify('kdj.j')
# "kdj.j:9,3,3,50.0"
```
And also
```py
from stock_pandas import
directive_stringify('kdj.j')
# "kdj.j:9,3,3,50.0"
```
Actually, `directive_stringify` does not rely on StockDataFrame instances.
### stock.rolling_calc(size, on, apply, forward, fill) -> np.ndarray
> Since 0.27.0
Applies a 1-D function along the given column or directive `on`
- **size** `int` the size of the rolling window
- **on** `str | Directive` along which the function should be applied
- **apply** `Callable[[np.ndarray], Any]` the 1-D function to apply
- **forward?** `bool = False` whether we should look backward (default value) to get each rolling window or not
- **fill?** `Any = np.nan` the value used to fill where there are not enough items to form a rolling window
```py
stock.rolling_calc(5, 'open', max)
# Whose return value equals to
stock['hhv:5,open'].to_numpy()
```
### stock.cumulate() -> StockDataFrame
Cumulate the current data frame `stock` based on its time frame setting
```py
StockDataFrame(one_minute_kline_data_frame, time_frame='5m').cumulate()
# And you will get a 5-minute kline data
```
see [Cumulation and DatetimeIndex][cumulation] for details
### stock.cum_append(other: DataFrame) -> StockDataFrame
Append `other` to the end of the current data frame `stock` and apply cumulation on them. And the following slice of code is equivalent to the above one:
```py
StockDataFrame(time_frame='5m').cum_append(one_minute_kline_data_frame)
```
see [Cumulation and DatetimeIndex][cumulation] for details
### stock.fulfill() -> self
> Since 1.2.0
Fulfill all stock indicator columns. By default, adding new rows to a `StockDataFrame` will not update stock indicators of the new row.
Stock indicators will only be updated when accessing the stock indicator column or calling `stock.fulfill()`
Check the [test cases](https://github.com/kaelzhang/stock-pandas/blob/master/test/test_fulfill.py) for details
### directive_stringify(directive_str) -> str
> since 0.30.0
Similar to `stock.directive_stringify()` but could be called without class initialization
```py
from stock_pandas import directive_stringify
directive_stringify('boll')
# boll:21,close
```
## Cumulation and DatetimeIndex
Suppose we have a csv file containing kline data of a stock in 1-minute time frame
```py
csv = pd.read_csv(csv_path)
print(csv)
```
```
date open high low close volume
0 2020-01-01 00:00:00 329.4 331.6 327.6 328.8 14202519
1 2020-01-01 00:01:00 330.0 332.0 328.0 331.0 13953191
2 2020-01-01 00:02:00 332.8 332.8 328.4 331.0 10339120
3 2020-01-01 00:03:00 332.0 334.2 330.2 331.0 9904468
4 2020-01-01 00:04:00 329.6 330.2 324.9 324.9 13947162
5 2020-01-01 00:04:00 329.6 330.2 324.8 324.8 13947163 <- There is an update of
2020-01-01 00:04:00
...
16 2020-01-01 00:16:00 333.2 334.8 331.2 334.0 12428539
17 2020-01-01 00:17:00 333.0 333.6 326.8 333.6 15533405
18 2020-01-01 00:18:00 335.0 335.2 326.2 327.2 16655874
19 2020-01-01 00:19:00 327.0 327.2 322.0 323.0 15086985
```
> Noted that duplicated records of a same timestamp will not be cumulated. The records except the latest one will be disgarded.
```py
stock = StockDataFrame(
csv,
date_col='date',
# Which is equivalent to `time_frame=TimeFrame.M5`
time_frame='5m'
)
print(stock)
```
```
open high low close volume
2020-01-01 00:00:00 329.4 331.6 327.6 328.8 14202519
2020-01-01 00:01:00 330.0 332.0 328.0 331.0 13953191
2020-01-01 00:02:00 332.8 332.8 328.4 331.0 10339120
2020-01-01 00:03:00 332.0 334.2 330.2 331.0 9904468
2020-01-01 00:04:00 329.6 330.2 324.9 324.9 13947162
2020-01-01 00:04:00 329.6 330.2 324.8 324.8 13947162
...
2020-01-01 00:16:00 333.2 334.8 331.2 334.0 12428539
2020-01-01 00:17:00 333.0 333.6 326.8 333.6 15533405
2020-01-01 00:18:00 335.0 335.2 326.2 327.2 16655874
2020-01-01 00:19:00 327.0 327.2 322.0 323.0 15086985
```
You must have figured it out that the data frame now has [`DatetimeIndex`es][datetimeindex].
But it will not become a 15-minute kline data unless we cumulate it, and only cumulates new frames if you use `stock.cum_append(them)` to cumulate `them`.
```py
stock_15m = stock.cumulate()
print(stock_15m)
```
Now we get a 15-minute kline
```
open high low close volume
2020-01-01 00:00:00 329.4 334.2 324.8 324.8 62346461.0
2020-01-01 00:05:00 325.0 327.8 316.2 322.0 82176419.0
2020-01-01 00:10:00 323.0 327.8 314.6 327.6 74409815.0
2020-01-01 00:15:00 330.0 335.2 322.0 323.0 82452902.0
```
For more details and about how to get full control of everything, check the online Google Colab notebook here.
## Syntax of `directive`
```ebnf
directive := command | command operator expression
operator := '/' | '\' | '><' | '<' | '<=' | '==' | '>=' | '>'
expression := float | command
command := command_name | command_name : arguments
command_name := main_command_name | main_command_name.sub_command_name
main_command_name := alphabets
sub_command_name := alphabets
arguments := argument | argument , arguments
argument := empty_string | string | ( directive )
```
#### `directive` Example
Here lists several use cases of column names
```py
# The middle band of bollinger bands
# which is actually a 20-period (default) moving average
stock['boll']
# kdj j less than 0
# This returns a series of bool type
stock['kdj.j < 0']
# kdj %K cross up kdj %D
stock['kdj.k / kdj.d']
# 5-period simple moving average
stock['ma:5']
# 10-period simple moving average on open prices
stock['ma:10,open']
# Dataframe of 5-period, 10-period, 30-period ma
stock[[
'ma:5',
'ma:10',
'ma:30'
]]
# Which means we use the default values of the first and the second parameters,
# and specify the third parameter
stock['macd:,,10']
# We must wrap a parameter which is a nested command or directive
stock['increase:(ma:20,close),3']
# stock-pandas has a powerful directive parser,
# so we could even write directives like this:
stock['''
repeat
:
(
column:close > boll.upper
),
5
''']
```
## Built-in Commands of Indicators
Document syntax explanation:
- **param0** `int` which means `param0` is a required parameter of type `int`.
- **param1?** `str='close'` which means parameter `param1` is optional with default value `'close'`.
Actually, all parameters of a command are of string type, so the `int` here means an interger-like string.
### `ma`, simple Moving Averages
```
ma:<period>,<column>
```
Gets the `period`-period simple moving average on column named `column`.
`SMA` is often confused between simple moving average and smoothed moving average.
So `stock-pandas` will use `ma` for simple moving average and `smma` for smoothed moving average.
- **period** `int` (required)
- **column?** `enum<'open'|'high'|'low'|'close'>='close'` Which column should the calculation based on. Defaults to `'close'`
```py
# which is equivalent to `stock['ma:5,close']`
stock['ma:5']
stock['ma:10,open']
```
### `ema`, Exponential Moving Average
```
ema:<period>,<column>
```
Gets the Exponential Moving Average, also known as the Exponential Weighted Moving Average.
The arguments of this command is the same as `ma`.
### `macd`, Moving Average Convergence Divergence
```
macd:<fast_period>,<slow_period>
macd.signal:<fast_period>,<slow_period>,<signal_period>
macd.histogram:<fast_period>,<slow_period>,<signal_period>
```
- **fast_period?** `int=12` fast period (short period). Defaults to `12`.
- **slow_period?** `int=26` slow period (long period). Defaults to `26`
- **signal_period?** `int=9` signal period. Defaults to `9`
```py
# macd
stock['macd']
stock['macd.dif']
# macd signal band, which is a shortcut for stock['macd.signal']
stock['macd.s']
stock['macd.signal']
stock['macd.dea']
# macd histogram band, which is equivalent to stock['macd.h']
stock['macd.histogram']
stock['macd.h']
stock['macd.macd']
```
### `boll`, BOLLinger bands
```
boll:<period>,<column>
boll.upper:<period>,<times>,<column>
boll.lower:<period>,<times>,<column>
```
- **period?** `int=20`
- **times?** `float=2.`
- **column?** `str='close'`
```py
# boll
stock['boll']
# bollinger upper band, a shortcut for stock['boll.upper']
stock['boll.u']
stock['boll.upper']
# bollinger lower band, which is equivalent to stock['boll.l']
stock['boll.lower']
stock['boll.l']
```
### `rsv`, Raw Stochastic Value
```
rsv:<period>
```
Calculates the raw stochastic value which is often used to calculate KDJ
### `kdj`, a variety of stochastic oscillator
The variety of [Stochastic Oscillator](https://en.wikipedia.org/wiki/Stochastic_oscillator) indicator created by [Dr. George Lane](https://en.wikipedia.org/wiki/George_Lane_(technical_analyst)), which follows the formula:
```
RSV = rsv(period_rsv)
%K = ema(RSV, period_k)
%D = ema(%K, period_d)
%J = 3 * %K - 2 * %D
```
And the `ema` here is the exponential weighted moving average with initial value as `init_value`.
PAY ATTENTION that the calculation forumla is different from wikipedia, but it is much popular and more widely used by the industry.
**Directive Arguments**:
```
kdj.k:<period_rsv>,<period_k>,<init_value>
kdj.d:<period_rsv>,<period_k>,<period_d>,<init_value>
kdj.j:<period_rsv>,<period_k>,<period_d>,<init_value>
```
- **period_rsv?** `int=9` The period for calculating RSV, which is used for K%
- **period_k?** `int=3` The period for calculating the EMA of RSV, which is used for K%
- **period_d?** `int=3` The period for calculating the EMA of K%, which is used for D%
- **init_value?** `float=50.0` The initial value for calculating ema. Trading softwares of different companies usually use different initial values each of which is usually `0.0`, `50.0` or `100.0`.
```py
# The %D series of KDJ
stock['kdj.d']
# which is equivalent to
stock['kdj.d:9,3,3,50.0']
# The KDJ serieses of with parameters 9, 9, and 9
stock[['kdj.k:9,9', 'kdj.d:9,9,9', 'kdj.j:9,9,9']]
```
### `kdjc`, another variety of stochastic oscillator
Unlike `kdj`, `kdjc` uses **close** value instead of high and low value to calculate `rsv`, which makes the indicator more sensitive than `kdj`
The arguments of `kdjc` are the same as `kdj`
### `rsi`, Relative Strength Index
```
rsi:<period>
```
Calculates the N-period RSI (Relative Strength Index)
- **period** `int` The period to calculate RSI. `period` should be an int which is larger than `1`
### `bbi`, Bull and Bear Index
```
bbi:<a>,<b>,<c>,<d>
```
Calculates indicator BBI (Bull and Bear Index) which is the average of `ma:3`, `ma:6`, `ma:12`, `ma:24` by default
- **a?** `int=3`
- **b?** `int=6`
- **c?** `int=12`
- **d?** `int=24`
### `llv`, Lowest of Low Values
```
llv:<period>,<column>
```
Gets the lowest of low prices in N periods
- **period** `int`
- **column?** `str='low'` Defaults to `'low'`. But you could also get the lowest value of close prices
```py
# The 10-period lowest prices
stock['llv:10']
# The 10-period lowest close prices
stock['llv:10,close']
```
### `hhv`, Highest of High Values
```
hhv:<period>,<column>
```
Gets the highest of high prices in N periods. The arguments of `hhv` is the same as `llv`
## Built-in Commands for Statistics
### `column`
```
column:<name>
```
Just gets the series of a column. This command is designed to be used together with an operator to compare with another command or as a parameter of some statistics command.
- **name** `str` the name of the column
```py
# A bool-type series indicates whether the current price is higher than the upper bollinger band
stock['column:close > boll.upper']
```
### `increase`
```
increase:<on>,<repeat>,<step>
```
Gets a `bool`-type series each item of which is `True` if the value of indicator `on` increases in the last `period`-period.
- **on** `str` the command name of an indicator on what the calculation should be based
- **repeat?** `int=1`
- **direction?** `1 | -1` the direction of "increase". `-1` means decreasing
For example:
```py
# Which means whether the `ma:20,close` line
# (a.k.a. 20-period simple moving average on column `'close'`)
# has been increasing repeatedly for 3 times (maybe 3 days)
stock['increase:(ma:20,close),3']
# If the close price has been decreasing repeatedly for 5 times (maybe 5 days)
stock['increase:close,5,-1']
```
### `style`
```
style:<style>
```
Gets a `bool`-type series whether the candlestick of a period is of `style` style
- **style** `'bullish' | 'bearish'`
```py
stock['style:bullish']
```
### `repeat`
```
repeat:(<bool_directive>),<repeat>
```
The `repeat` command first gets the result of directive `bool_directive`, and detect whether `True` is repeated for `repeat` times
- **bool_directive** `str` the directive which should returns a series of `bool`s. PAY ATTENTION, that the directive should be wrapped with parantheses as a parameter.
- **repeat?** `int=1` which should be larger than `0`
```py
# Whether the bullish candlestick repeats for 3 periods (maybe 3 days)
stock['repeat:(style:bullish),3']
```
### `change`
```
change:<on>,<period>
```
Percentage change between the current and a prior element on a certain series
Computes the percentage change from the immediately previous element by default. This is useful in comparing the percentage of change in a time series of prices.
- **on** `str` the directive which returns a series of numbers, and the calculation will based on the series.
- **period?** `int=2` `2` means we computes with the start value and the end value of a 2-period window.
```py
# Percentage change of 20-period simple moving average
stock['change:(ma:20)']
```
## Operators
```
left operator right
```
### Operator: `/`
whether `left` crosses through `right` from the down side of `right` to the upper side which we call it as "cross up".
### Operator: `\`
whether `left` crosses down `right`.
```py
# Which we call them "dead crosses"
stock['macd \\ macd.signal']
```
**PAY ATTENTION**, in the example above, we should escape the backslash, so we've got double backslashes `'\\'`
### Operator: `><`
whether `left` crosses `right`, either up or down.
### Operator: `<` | `<=` | `==` | `>=` | `>`
For a certain record of the same time, whether the value of `left` is less than / less than or equal to / equal to / larger than or equal to / larger than the value of `right`.
## Errors
```py
from stock_pandas import (
DirectiveSyntaxError,
DirectiveValueError
)
```
### `DirectiveSyntaxError`
Raises if there is a syntax error in the given directive.
```py
stock['''
repeat
:
(
column:close >> boll.upper
),
5
''']
```
`DirectiveSyntaxError` might print some messages like this:
```
File "<string>", line 5, column 26
repeat
:
(
> column:close >> boll.upper
),
5
^
DirectiveSyntaxError: ">>" is an invalid operator
```
### `DirectiveValueError`
Raises if
- there is an unknown command name
- something is wrong about the command arguments
- etc.
****
## Advanced Sections
> How to extend stock-pandas and support more indicators,
> This section is only recommended for contributors, but not for normal users, for that the definition of `COMMANDS` might change in the future.
```py
from stock_pandas import COMMANDS, CommandPreset
```
To add a new indicator to stock-pandas, you could update the `COMMANDS` dict.
```py
# The value of 'new-indicator' is a tuple
COMMANDS['new-indicator'] = (
# The first item of the tuple is a CommandPreset instance
CommandPreset(
formula,
args_setting
),
sub_commands_dict,
aliases_of_sub_commands
)
```
You could check [here](https://github.com/kaelzhang/stock-pandas/blob/master/stock_pandas/commands/base.py#L54) to figure out the typings for `COMMANDS`.
For a simplest indicator, such as simple moving average, you could check the implementation [here](https://github.com/kaelzhang/stock-pandas/blob/master/stock_pandas/commands/trend_following.py#L60).
### formula(df, s, *args) -> Tuple[np.ndarray, int]
`formula` is a `Callable[[StockDataFrame, slice, ...], [ndarray, int]]`.
- **df** `StockDataFrame` the first argument of `formula` is the stock dataframe itself
- **s** `slice` sometimes, we don't need to calculate the whole dataframe but only part of it. This argument is passed into the formula by `stock_pandas` and should not be changed manually.
- **args** `Tuple[Any]` the args of the indicator which is defined by `args_setting`
The Callable returns a tuple:
- The first item of the tuple is the calculated result which is a numpy ndarray.
- The second item of the tuple is the mininum periods to calculate the indicator.
### args_setting: [(default, validate_and_coerce), ...]
`args_setting` is a list of tuples.
- The first item of each tuple is the default value of the parameter, and it could be `None` which implies it has no default value and is required.
- The second item is a raisable callable which receives user input, validates it, coerces the type of the value and returns it. If the parameter has a default value and user don't specified a value, the function will be skipped.
### sub_commands_dict: Dict[str, CommandPreset]
A dict to declare sub commands, such as `boll.upper`.
`sub_commands_dict` could be `None` which indicates the indicator has no sub commands
### aliases_of_sub_commands: Dict[str, Optional[str]]
Which declares the shortcut or alias of the commands, such as `boll.u`
```py
dict(
u='upper'
)
```
If the value of an alias is `None`, which means it is an alias of the main command, such as `macd.dif`
```py
dict(
dif=None
)
```
## Development
First, install conda (recommended), and generate a conda environment for this project
```sh
conda create -n stock-pandas python=3.11
conda activate stock-pandas
# Install requirements
make install
# Build python ext (C++)
make build-ext
# Run unit tests
make test
```
Raw data
{
"_id": null,
"home_page": "https://github.com/kaelzhang/stock-pandas",
"name": "stock-pandas",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": "",
"keywords": "pandas pandas-dataframe stock stat indicators macd",
"author": "Kael Zhang",
"author_email": "i+pypi@kael.me",
"download_url": "https://files.pythonhosted.org/packages/a8/09/72f88a0922439e74a8cb570f8e747caef1a3a73925af02061f56e1bc9e5b/stock-pandas-1.2.0.tar.gz",
"platform": null,
"description": "[![](https://travis-ci.org/kaelzhang/stock-pandas.svg?branch=master)](https://travis-ci.org/kaelzhang/stock-pandas)\n[![](https://codecov.io/gh/kaelzhang/stock-pandas/branch/master/graph/badge.svg)](https://codecov.io/gh/kaelzhang/stock-pandas)\n[![](https://img.shields.io/pypi/v/stock-pandas.svg)](https://pypi.org/project/stock-pandas/)\n[![](https://img.shields.io/pypi/l/stock-pandas.svg)](https://github.com/kaelzhang/stock-pandas)\n\n\n[ma]: #ma-simple-moving-averages\n[ema]: #ema-exponential-moving-average\n[macd]: #macd-moving-average-convergence-divergence\n[boll]: #boll-bollinger-bands\n[rsv]: #rsv-raw-stochastic-value\n[kdj]: #kdj-a-variety-of-stochastic-oscillator\n[kdj]: #kdjc-another-variety-of-stochastic-oscillator\n[rsi]: #rsi-relative-strength-index\n[bbi]: #bbi-bull-and-bear-index\n[llv]: #llv-lowest-of-low-values\n[hhv]: #hhv-highest-of-high-values\n[column]: #column\n[increase]: #increase\n[style]: #style\n[repeat]: #repeat\n[change]: #change\n[cumulation]: #cumulation-and-datetimeindex\n[datetimeindex]: https://pandas.pydata.org/docs/reference/api/pandas.DatetimeIndex.html\n\n\n# [stock-pandas](https://github.com/kaelzhang/stock-pandas)\n\n**stock-pandas** inherits and extends `pandas.DataFrame` to support:\n- Stock Statistics\n- Stock Indicators, including:\n - Trend-following momentum indicators, such as [**MA**][ma], [**EMA**][ema], [**MACD**][macd], [**BBI**][bbi]\n - Dynamic support and resistance indicators, such as [**BOLL**][boll]\n - Over-bought / over-sold indicators, such as [**KDJ**][kdj], [**RSI**][rsi]\n - Other indicators, such as [**LLV**][llv], [**HHV**][hhv]\n - For more indicators, welcome to [request a proposal](https://github.com/kaelzhang/stock-pandas/issues/new?assignees=&labels=feature&template=FEATURE_REQUEST.md&title=), or fork and send me a pull request, or extend stock-pandas yourself. You might read the [Advanced Sections](https://github.com/kaelzhang/stock-pandas#advanced-sections) below.\n- To [cumulate][cumulation] kline data based on a given time frame, so that it could easily handle real-time data updates.\n\n`stock-pandas` makes automatical trading much easier. `stock-pandas` requires Python >= **3.6** and Pandas >= **1.0.0**(for now)\n\nWith the help of `stock-pandas` and mplfinance, we could easily draw something like:\n\n![](boll.png)\n\nThe code example is available at [here](https://github.com/kaelzhang/stock-pandas-examples/blob/master/example/bollinger_bands.ipynb).\n\n## Install\n\nFor now, before installing `stock-pandas` in your environment\n\n#### Have `g++` compiler installed\n\n```sh\n# With yum, for CentOS, Amazon Linux, etc\nyum install gcc-c++\n\n# With apt-get, for Ubuntu\napt-get install g++\n\n# For macOS, install XCode commandline tools\nxcode-select --install\n```\n\nIf you use docker with `Dockerfile` and use python image,\n\n```Dockerfile\nFROM python:3.8\n\n...\n```\n\nThe default `python:3.8` image already contains g++, so we do not install g++ additionally.\n\n#### Install `stock-pandas`\n\n```sh\n# Installing `stock-pandas` requires `numpy` to be installed first\npip install numpy\n\npip install stock-pandas\n```\n\nBe careful, you still need to install `numpy` explicitly even if `numpy` and `stock-pandas` both are contained in `requirement.txt`\n\n```txt\nnumpy\nstock-pandas\nother-dependencies\n...\n```\n\n```sh\npip install numpy\n\npip install -r requirement.txt\n```\n\n## Usage\n\n```py\nfrom stock_pandas import StockDataFrame\n\n# or\nimport stock_pandas as spd\n```\n\nWe also have some examples with annotations in the [`example`](https://github.com/kaelzhang/stock-pandas/tree/master/example) directory, you could use [JupyterLab](https://jupyter.org/) or Jupyter notebook to play with them.\n\n### StockDataFrame\n\n`StockDataFrame` inherits from `pandas.DataFrame`, so if you are familiar with `pandas.DataFrame`, you are already ready to use `stock-pandas`\n\n```py\nimport pandas as pd\nstock = StockDataFrame(pd.read_csv('stock.csv'))\n```\n\nAs we know, we could use `[]`, which called **pandas indexing** (a.k.a. `__getitem__` in python) to select out lower-dimensional slices. In addition to indexing with `colname` (column name of the `DataFrame`), we could also do indexing by `directive`s.\n\n```py\nstock[directive] # Gets a pandas.Series\n\nstock[[directive0, directive1]] # Gets a StockDataFrame\n```\n\nWe have an example to show the most basic indexing using `[directive]`\n\n```py\nstock = StockDataFrame({\n 'open' : ...,\n 'high' : ...,\n 'low' : ...,\n 'close': [5, 6, 7, 8, 9]\n})\n\nstock['ma:2']\n\n# 0 NaN\n# 1 5.5\n# 2 6.5\n# 3 7.5\n# 4 8.5\n# Name: ma:2,close, dtype: float64\n```\n\nWhich prints the 2-period simple moving average on column `\"close\"`.\n\n#### Parameters\n\n- **date_col** `Optional[str] = None` If set, then the column named `date_col` will convert and set as [`DateTimeIndex`](datetimeindex) of the data frame\n- **to_datetime_kwargs** `dict = {}` the keyworded arguments to be passed to `pandas.to_datetime()`. It only takes effect if `date_col` is specified.\n- **time_frame** `str | TimeFrame | None = None` time frame of the stock. For now, only the following time frames are supported:\n - `'1m'` or `TimeFrame.M1`\n - `'3m'` or `TimeFrame.M3`\n - `'5m'` or `TimeFrame.M5`\n - `'15m'` or `TimeFrame.M15`\n - `'30m'` or `TimeFrame.M30`\n - `'1h'` or `TimeFrame.H1`\n - `'2h'` or `TimeFrame.H2`\n - `'4h'` or `TimeFrame.H4`\n - `'6h'` or `TimeFrame.H6`\n - `'8h'` or `TimeFrame.H8`\n - `'12h'` or `TimeFrame.H12`\n\n### stock.exec(directive: str, create_column: bool=False) -> np.ndarray\n\nExecutes the given directive and returns a numpy ndarray according to the directive.\n\n```py\nstock['ma:5'] # returns a Series\n\nstock.exec('ma:5', create_column=True) # returns a numpy ndarray\n```\n\n```py\n# This will only calculate without creating a new column in the dataframe\nstock.exec('ma:20')\n```\n\nThe difference between `stock[directive]` and `stock.exec(directive)` is that\n- the former will create a new column for the result of `directive` as a cache for later use, while `stock.exec(directive)` does not unless we pass the parameter `create_column` as `True`\n- the former one accepts other pandas indexing targets, while `stock.exec(directive)` only accepts a valid **stock-pandas** directive string\n- the former one returns a `pandas.Series` or `StockDataFrame` object while the latter one returns an [`np.ndarray`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html)\n\n### stock.alias(alias: str, name: str) -> None\n\nDefines column alias or directive alias\n\n- **alias** `str` the alias name\n- **name** `str` the name of an existing column or the directive string\n\n```py\n# Some plot library such as `mplfinance` requires a column named capitalized `Open`,\n# but it is ok, we could create an alias.\nstock.alias('Open', 'open')\n\nstock.alias('buy_point', 'kdj.j < 0')\n```\n\n### stock.get_column(key: str) -> pd.Series\n\nDirectly gets the column value by `key`, returns a pandas `Series`.\n\nIf the given `key` is an alias name, it will return the value of corresponding original column.\n\nIf the column is not found, a `KeyError` will be raised.\n\n```py\nstock = StockDataFrame({\n 'open' : ...,\n 'high' : ...,\n 'low' : ...,\n 'close': [5, 6, 7, 8, 9]\n})\n\nstock.get_column('close')\n# 0 5\n# 1 6\n# 2 7\n# 3 8\n# 4 9\n# Name: close, dtype: float64\n```\n\n```py\ntry:\n stock.get_column('Close')\nexcept KeyError as e:\n print(e)\n\n # KeyError: column \"Close\" not found\n\nstock.alias('Close', 'close')\n\nstock.get_column('Close')\n# The same as `stock.get_column('close')`\n```\n\n### stock.append(other, *args, **kwargs) -> StockDataFrame\n\nAppends rows of `other` to the end of caller, returning a new object.\n\nThis method has nearly the same hehavior of [`pandas.DataFrame.append()`](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.append.html), but instead it returns an instance of `StockDataFrame`, and it applies `date_col` to the newly-appended row(s) if possible.\n\n### stock.directive_stringify(directive: str) -> str\n\n> Since 0.26.0\n\nGets the full name of the `directive` which is also the actual column name of the data frame\n\n```py\nstock.directive_stringify('kdj.j')\n# \"kdj.j:9,3,3,50.0\"\n```\n\nAnd also\n\n```py\nfrom stock_pandas import\n\ndirective_stringify('kdj.j')\n# \"kdj.j:9,3,3,50.0\"\n```\n\nActually, `directive_stringify` does not rely on StockDataFrame instances.\n\n### stock.rolling_calc(size, on, apply, forward, fill) -> np.ndarray\n\n> Since 0.27.0\n\nApplies a 1-D function along the given column or directive `on`\n\n- **size** `int` the size of the rolling window\n- **on** `str | Directive` along which the function should be applied\n- **apply** `Callable[[np.ndarray], Any]` the 1-D function to apply\n- **forward?** `bool = False` whether we should look backward (default value) to get each rolling window or not\n- **fill?** `Any = np.nan` the value used to fill where there are not enough items to form a rolling window\n\n```py\nstock.rolling_calc(5, 'open', max)\n\n# Whose return value equals to\nstock['hhv:5,open'].to_numpy()\n```\n\n### stock.cumulate() -> StockDataFrame\n\nCumulate the current data frame `stock` based on its time frame setting\n\n```py\nStockDataFrame(one_minute_kline_data_frame, time_frame='5m').cumulate()\n\n# And you will get a 5-minute kline data\n```\n\nsee [Cumulation and DatetimeIndex][cumulation] for details\n\n### stock.cum_append(other: DataFrame) -> StockDataFrame\n\nAppend `other` to the end of the current data frame `stock` and apply cumulation on them. And the following slice of code is equivalent to the above one:\n\n```py\nStockDataFrame(time_frame='5m').cum_append(one_minute_kline_data_frame)\n```\n\nsee [Cumulation and DatetimeIndex][cumulation] for details\n\n\n### stock.fulfill() -> self\n\n> Since 1.2.0\n\nFulfill all stock indicator columns. By default, adding new rows to a `StockDataFrame` will not update stock indicators of the new row.\n\nStock indicators will only be updated when accessing the stock indicator column or calling `stock.fulfill()`\n\nCheck the [test cases](https://github.com/kaelzhang/stock-pandas/blob/master/test/test_fulfill.py) for details\n\n### directive_stringify(directive_str) -> str\n\n> since 0.30.0\n\nSimilar to `stock.directive_stringify()` but could be called without class initialization\n\n```py\nfrom stock_pandas import directive_stringify\n\ndirective_stringify('boll')\n# boll:21,close\n```\n\n## Cumulation and DatetimeIndex\n\nSuppose we have a csv file containing kline data of a stock in 1-minute time frame\n\n```py\ncsv = pd.read_csv(csv_path)\n\nprint(csv)\n```\n\n```\n date open high low close volume\n0 2020-01-01 00:00:00 329.4 331.6 327.6 328.8 14202519\n1 2020-01-01 00:01:00 330.0 332.0 328.0 331.0 13953191\n2 2020-01-01 00:02:00 332.8 332.8 328.4 331.0 10339120\n3 2020-01-01 00:03:00 332.0 334.2 330.2 331.0 9904468\n4 2020-01-01 00:04:00 329.6 330.2 324.9 324.9 13947162\n5 2020-01-01 00:04:00 329.6 330.2 324.8 324.8 13947163 <- There is an update of\n 2020-01-01 00:04:00\n...\n16 2020-01-01 00:16:00 333.2 334.8 331.2 334.0 12428539\n17 2020-01-01 00:17:00 333.0 333.6 326.8 333.6 15533405\n18 2020-01-01 00:18:00 335.0 335.2 326.2 327.2 16655874\n19 2020-01-01 00:19:00 327.0 327.2 322.0 323.0 15086985\n```\n\n> Noted that duplicated records of a same timestamp will not be cumulated. The records except the latest one will be disgarded.\n\n\n```py\nstock = StockDataFrame(\n csv,\n date_col='date',\n # Which is equivalent to `time_frame=TimeFrame.M5`\n time_frame='5m'\n)\n\nprint(stock)\n```\n\n```\n open high low close volume\n2020-01-01 00:00:00 329.4 331.6 327.6 328.8 14202519\n2020-01-01 00:01:00 330.0 332.0 328.0 331.0 13953191\n2020-01-01 00:02:00 332.8 332.8 328.4 331.0 10339120\n2020-01-01 00:03:00 332.0 334.2 330.2 331.0 9904468\n2020-01-01 00:04:00 329.6 330.2 324.9 324.9 13947162\n2020-01-01 00:04:00 329.6 330.2 324.8 324.8 13947162\n...\n2020-01-01 00:16:00 333.2 334.8 331.2 334.0 12428539\n2020-01-01 00:17:00 333.0 333.6 326.8 333.6 15533405\n2020-01-01 00:18:00 335.0 335.2 326.2 327.2 16655874\n2020-01-01 00:19:00 327.0 327.2 322.0 323.0 15086985\n```\n\nYou must have figured it out that the data frame now has [`DatetimeIndex`es][datetimeindex].\n\nBut it will not become a 15-minute kline data unless we cumulate it, and only cumulates new frames if you use `stock.cum_append(them)` to cumulate `them`.\n\n```py\nstock_15m = stock.cumulate()\n\nprint(stock_15m)\n```\n\nNow we get a 15-minute kline\n\n```\n open high low close volume\n2020-01-01 00:00:00 329.4 334.2 324.8 324.8 62346461.0\n2020-01-01 00:05:00 325.0 327.8 316.2 322.0 82176419.0\n2020-01-01 00:10:00 323.0 327.8 314.6 327.6 74409815.0\n2020-01-01 00:15:00 330.0 335.2 322.0 323.0 82452902.0\n```\n\nFor more details and about how to get full control of everything, check the online Google Colab notebook here.\n\n## Syntax of `directive`\n\n```ebnf\ndirective := command | command operator expression\noperator := '/' | '\\' | '><' | '<' | '<=' | '==' | '>=' | '>'\nexpression := float | command\n\ncommand := command_name | command_name : arguments\ncommand_name := main_command_name | main_command_name.sub_command_name\nmain_command_name := alphabets\nsub_command_name := alphabets\n\narguments := argument | argument , arguments\nargument := empty_string | string | ( directive )\n```\n\n#### `directive` Example\n\nHere lists several use cases of column names\n\n```py\n# The middle band of bollinger bands\n# which is actually a 20-period (default) moving average\nstock['boll']\n\n# kdj j less than 0\n# This returns a series of bool type\nstock['kdj.j < 0']\n\n# kdj %K cross up kdj %D\nstock['kdj.k / kdj.d']\n\n# 5-period simple moving average\nstock['ma:5']\n\n# 10-period simple moving average on open prices\nstock['ma:10,open']\n\n# Dataframe of 5-period, 10-period, 30-period ma\nstock[[\n 'ma:5',\n 'ma:10',\n 'ma:30'\n]]\n\n# Which means we use the default values of the first and the second parameters,\n# and specify the third parameter\nstock['macd:,,10']\n\n# We must wrap a parameter which is a nested command or directive\nstock['increase:(ma:20,close),3']\n\n# stock-pandas has a powerful directive parser,\n# so we could even write directives like this:\nstock['''\nrepeat\n :\n (\n column:close > boll.upper\n ),\n 5\n''']\n```\n\n## Built-in Commands of Indicators\n\nDocument syntax explanation:\n\n- **param0** `int` which means `param0` is a required parameter of type `int`.\n- **param1?** `str='close'` which means parameter `param1` is optional with default value `'close'`.\n\nActually, all parameters of a command are of string type, so the `int` here means an interger-like string.\n\n### `ma`, simple Moving Averages\n\n```\nma:<period>,<column>\n```\n\nGets the `period`-period simple moving average on column named `column`.\n\n`SMA` is often confused between simple moving average and smoothed moving average.\n\nSo `stock-pandas` will use `ma` for simple moving average and `smma` for smoothed moving average.\n\n- **period** `int` (required)\n- **column?** `enum<'open'|'high'|'low'|'close'>='close'` Which column should the calculation based on. Defaults to `'close'`\n\n```py\n# which is equivalent to `stock['ma:5,close']`\nstock['ma:5']\n\nstock['ma:10,open']\n```\n\n### `ema`, Exponential Moving Average\n\n```\nema:<period>,<column>\n```\n\nGets the Exponential Moving Average, also known as the Exponential Weighted Moving Average.\n\nThe arguments of this command is the same as `ma`.\n\n### `macd`, Moving Average Convergence Divergence\n\n```\nmacd:<fast_period>,<slow_period>\nmacd.signal:<fast_period>,<slow_period>,<signal_period>\nmacd.histogram:<fast_period>,<slow_period>,<signal_period>\n```\n\n- **fast_period?** `int=12` fast period (short period). Defaults to `12`.\n- **slow_period?** `int=26` slow period (long period). Defaults to `26`\n- **signal_period?** `int=9` signal period. Defaults to `9`\n\n```py\n# macd\nstock['macd']\nstock['macd.dif']\n\n# macd signal band, which is a shortcut for stock['macd.signal']\nstock['macd.s']\nstock['macd.signal']\nstock['macd.dea']\n\n# macd histogram band, which is equivalent to stock['macd.h']\nstock['macd.histogram']\nstock['macd.h']\nstock['macd.macd']\n```\n\n### `boll`, BOLLinger bands\n\n```\nboll:<period>,<column>\nboll.upper:<period>,<times>,<column>\nboll.lower:<period>,<times>,<column>\n```\n\n- **period?** `int=20`\n- **times?** `float=2.`\n- **column?** `str='close'`\n\n```py\n# boll\nstock['boll']\n\n# bollinger upper band, a shortcut for stock['boll.upper']\nstock['boll.u']\nstock['boll.upper']\n\n# bollinger lower band, which is equivalent to stock['boll.l']\nstock['boll.lower']\nstock['boll.l']\n```\n\n### `rsv`, Raw Stochastic Value\n\n```\nrsv:<period>\n```\n\nCalculates the raw stochastic value which is often used to calculate KDJ\n\n### `kdj`, a variety of stochastic oscillator\n\nThe variety of [Stochastic Oscillator](https://en.wikipedia.org/wiki/Stochastic_oscillator) indicator created by [Dr. George Lane](https://en.wikipedia.org/wiki/George_Lane_(technical_analyst)), which follows the formula:\n\n```\nRSV = rsv(period_rsv)\n%K = ema(RSV, period_k)\n%D = ema(%K, period_d)\n%J = 3 * %K - 2 * %D\n```\n\nAnd the `ema` here is the exponential weighted moving average with initial value as `init_value`.\n\nPAY ATTENTION that the calculation forumla is different from wikipedia, but it is much popular and more widely used by the industry.\n\n**Directive Arguments**:\n\n```\nkdj.k:<period_rsv>,<period_k>,<init_value>\nkdj.d:<period_rsv>,<period_k>,<period_d>,<init_value>\nkdj.j:<period_rsv>,<period_k>,<period_d>,<init_value>\n```\n\n- **period_rsv?** `int=9` The period for calculating RSV, which is used for K%\n- **period_k?** `int=3` The period for calculating the EMA of RSV, which is used for K%\n- **period_d?** `int=3` The period for calculating the EMA of K%, which is used for D%\n- **init_value?** `float=50.0` The initial value for calculating ema. Trading softwares of different companies usually use different initial values each of which is usually `0.0`, `50.0` or `100.0`.\n\n```py\n# The %D series of KDJ\nstock['kdj.d']\n# which is equivalent to\nstock['kdj.d:9,3,3,50.0']\n\n# The KDJ serieses of with parameters 9, 9, and 9\nstock[['kdj.k:9,9', 'kdj.d:9,9,9', 'kdj.j:9,9,9']]\n```\n\n### `kdjc`, another variety of stochastic oscillator\n\nUnlike `kdj`, `kdjc` uses **close** value instead of high and low value to calculate `rsv`, which makes the indicator more sensitive than `kdj`\n\nThe arguments of `kdjc` are the same as `kdj`\n\n### `rsi`, Relative Strength Index\n\n```\nrsi:<period>\n```\n\nCalculates the N-period RSI (Relative Strength Index)\n\n- **period** `int` The period to calculate RSI. `period` should be an int which is larger than `1`\n\n### `bbi`, Bull and Bear Index\n\n```\nbbi:<a>,<b>,<c>,<d>\n```\n\nCalculates indicator BBI (Bull and Bear Index) which is the average of `ma:3`, `ma:6`, `ma:12`, `ma:24` by default\n\n- **a?** `int=3`\n- **b?** `int=6`\n- **c?** `int=12`\n- **d?** `int=24`\n\n### `llv`, Lowest of Low Values\n\n```\nllv:<period>,<column>\n```\n\nGets the lowest of low prices in N periods\n\n- **period** `int`\n- **column?** `str='low'` Defaults to `'low'`. But you could also get the lowest value of close prices\n\n```py\n# The 10-period lowest prices\nstock['llv:10']\n\n# The 10-period lowest close prices\nstock['llv:10,close']\n```\n\n### `hhv`, Highest of High Values\n\n```\nhhv:<period>,<column>\n```\n\nGets the highest of high prices in N periods. The arguments of `hhv` is the same as `llv`\n\n## Built-in Commands for Statistics\n\n### `column`\n\n```\ncolumn:<name>\n```\n\nJust gets the series of a column. This command is designed to be used together with an operator to compare with another command or as a parameter of some statistics command.\n\n- **name** `str` the name of the column\n\n```py\n# A bool-type series indicates whether the current price is higher than the upper bollinger band\nstock['column:close > boll.upper']\n```\n\n### `increase`\n\n```\nincrease:<on>,<repeat>,<step>\n```\n\nGets a `bool`-type series each item of which is `True` if the value of indicator `on` increases in the last `period`-period.\n\n- **on** `str` the command name of an indicator on what the calculation should be based\n- **repeat?** `int=1`\n- **direction?** `1 | -1` the direction of \"increase\". `-1` means decreasing\n\nFor example:\n\n```py\n# Which means whether the `ma:20,close` line\n# (a.k.a. 20-period simple moving average on column `'close'`)\n# has been increasing repeatedly for 3 times (maybe 3 days)\nstock['increase:(ma:20,close),3']\n\n# If the close price has been decreasing repeatedly for 5 times (maybe 5 days)\nstock['increase:close,5,-1']\n```\n\n### `style`\n\n```\nstyle:<style>\n```\n\nGets a `bool`-type series whether the candlestick of a period is of `style` style\n\n- **style** `'bullish' | 'bearish'`\n\n```py\nstock['style:bullish']\n```\n\n### `repeat`\n\n```\nrepeat:(<bool_directive>),<repeat>\n```\n\nThe `repeat` command first gets the result of directive `bool_directive`, and detect whether `True` is repeated for `repeat` times\n\n- **bool_directive** `str` the directive which should returns a series of `bool`s. PAY ATTENTION, that the directive should be wrapped with parantheses as a parameter.\n- **repeat?** `int=1` which should be larger than `0`\n\n```py\n# Whether the bullish candlestick repeats for 3 periods (maybe 3 days)\nstock['repeat:(style:bullish),3']\n```\n\n### `change`\n\n```\nchange:<on>,<period>\n```\n\nPercentage change between the current and a prior element on a certain series\n\nComputes the percentage change from the immediately previous element by default. This is useful in comparing the percentage of change in a time series of prices.\n\n- **on** `str` the directive which returns a series of numbers, and the calculation will based on the series.\n- **period?** `int=2` `2` means we computes with the start value and the end value of a 2-period window.\n\n```py\n# Percentage change of 20-period simple moving average\nstock['change:(ma:20)']\n```\n\n## Operators\n\n```\nleft operator right\n```\n\n### Operator: `/`\n\nwhether `left` crosses through `right` from the down side of `right` to the upper side which we call it as \"cross up\".\n\n### Operator: `\\`\n\nwhether `left` crosses down `right`.\n\n```py\n# Which we call them \"dead crosses\"\nstock['macd \\\\ macd.signal']\n```\n\n**PAY ATTENTION**, in the example above, we should escape the backslash, so we've got double backslashes `'\\\\'`\n\n### Operator: `><`\n\nwhether `left` crosses `right`, either up or down.\n\n### Operator: `<` | `<=` | `==` | `>=` | `>`\n\nFor a certain record of the same time, whether the value of `left` is less than / less than or equal to / equal to / larger than or equal to / larger than the value of `right`.\n\n## Errors\n\n```py\nfrom stock_pandas import (\n DirectiveSyntaxError,\n DirectiveValueError\n)\n```\n\n### `DirectiveSyntaxError`\n\nRaises if there is a syntax error in the given directive.\n\n```py\nstock['''\nrepeat\n :\n (\n column:close >> boll.upper\n ),\n 5\n''']\n```\n\n`DirectiveSyntaxError` might print some messages like this:\n\n```\nFile \"<string>\", line 5, column 26\n\n repeat\n :\n (\n> column:close >> boll.upper\n ),\n 5\n\n ^\nDirectiveSyntaxError: \">>\" is an invalid operator\n```\n\n### `DirectiveValueError`\n\nRaises if\n- there is an unknown command name\n- something is wrong about the command arguments\n- etc.\n\n****\n\n## Advanced Sections\n\n> How to extend stock-pandas and support more indicators,\n\n> This section is only recommended for contributors, but not for normal users, for that the definition of `COMMANDS` might change in the future.\n\n```py\nfrom stock_pandas import COMMANDS, CommandPreset\n```\n\nTo add a new indicator to stock-pandas, you could update the `COMMANDS` dict.\n\n```py\n# The value of 'new-indicator' is a tuple\nCOMMANDS['new-indicator'] = (\n # The first item of the tuple is a CommandPreset instance\n CommandPreset(\n formula,\n args_setting\n ),\n sub_commands_dict,\n aliases_of_sub_commands\n)\n```\n\nYou could check [here](https://github.com/kaelzhang/stock-pandas/blob/master/stock_pandas/commands/base.py#L54) to figure out the typings for `COMMANDS`.\n\nFor a simplest indicator, such as simple moving average, you could check the implementation [here](https://github.com/kaelzhang/stock-pandas/blob/master/stock_pandas/commands/trend_following.py#L60).\n\n### formula(df, s, *args) -> Tuple[np.ndarray, int]\n\n`formula` is a `Callable[[StockDataFrame, slice, ...], [ndarray, int]]`.\n\n- **df** `StockDataFrame` the first argument of `formula` is the stock dataframe itself\n- **s** `slice` sometimes, we don't need to calculate the whole dataframe but only part of it. This argument is passed into the formula by `stock_pandas` and should not be changed manually.\n- **args** `Tuple[Any]` the args of the indicator which is defined by `args_setting`\n\nThe Callable returns a tuple:\n- The first item of the tuple is the calculated result which is a numpy ndarray.\n- The second item of the tuple is the mininum periods to calculate the indicator.\n\n### args_setting: [(default, validate_and_coerce), ...]\n\n`args_setting` is a list of tuples.\n\n- The first item of each tuple is the default value of the parameter, and it could be `None` which implies it has no default value and is required.\n\n- The second item is a raisable callable which receives user input, validates it, coerces the type of the value and returns it. If the parameter has a default value and user don't specified a value, the function will be skipped.\n\n### sub_commands_dict: Dict[str, CommandPreset]\n\nA dict to declare sub commands, such as `boll.upper`.\n\n`sub_commands_dict` could be `None` which indicates the indicator has no sub commands\n\n### aliases_of_sub_commands: Dict[str, Optional[str]]\n\nWhich declares the shortcut or alias of the commands, such as `boll.u`\n\n```py\ndict(\n u='upper'\n)\n```\n\nIf the value of an alias is `None`, which means it is an alias of the main command, such as `macd.dif`\n\n```py\ndict(\n dif=None\n)\n```\n\n## Development\n\nFirst, install conda (recommended), and generate a conda environment for this project\n\n```sh\nconda create -n stock-pandas python=3.11\n\nconda activate stock-pandas\n\n# Install requirements\nmake install\n\n# Build python ext (C++)\nmake build-ext\n\n# Run unit tests\nmake test\n```\n\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "The wrapper of `pandas.DataFrame` with stock statistics and indicators support.",
"version": "1.2.0",
"split_keywords": [
"pandas",
"pandas-dataframe",
"stock",
"stat",
"indicators",
"macd"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "410a42e080029f437a69b0f8e0bd8747118648414a9754a5226ecc8185a99254",
"md5": "eb7182ab5ab73869375c5bd3775b36e3",
"sha256": "2e0e0804df0cd5768d99106ad7df8d1760556a9f204be570de0315666359068f"
},
"downloads": -1,
"filename": "stock_pandas-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl",
"has_sig": false,
"md5_digest": "eb7182ab5ab73869375c5bd3775b36e3",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": ">=3.6",
"size": 120194,
"upload_time": "2023-04-07T01:59:40",
"upload_time_iso_8601": "2023-04-07T01:59:40.910822Z",
"url": "https://files.pythonhosted.org/packages/41/0a/42e080029f437a69b0f8e0bd8747118648414a9754a5226ecc8185a99254/stock_pandas-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a80972f88a0922439e74a8cb570f8e747caef1a3a73925af02061f56e1bc9e5b",
"md5": "0b95fbf4c9e61754049228a988f48a16",
"sha256": "c54cfe9b89c85bba0feeb0d4fc0081211cd4e3f865f465317d2131a90dc44993"
},
"downloads": -1,
"filename": "stock-pandas-1.2.0.tar.gz",
"has_sig": false,
"md5_digest": "0b95fbf4c9e61754049228a988f48a16",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 164062,
"upload_time": "2023-04-07T01:59:43",
"upload_time_iso_8601": "2023-04-07T01:59:43.241853Z",
"url": "https://files.pythonhosted.org/packages/a8/09/72f88a0922439e74a8cb570f8e747caef1a3a73925af02061f56e1bc9e5b/stock-pandas-1.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-04-07 01:59:43",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "kaelzhang",
"github_project": "stock-pandas",
"travis_ci": true,
"coveralls": false,
"github_actions": false,
"requirements": [],
"lcname": "stock-pandas"
}