tksbrokerapi.TradeRoutines

This library contains some methods used by trade scenarios implemented with TKSBrokerAPI module.

  1# -*- coding: utf-8 -*-
  2# Author: Timur Gilmullin
  3
  4"""
  5This library contains some methods used by trade scenarios implemented with TKSBrokerAPI module.
  6
  7- **TKSBrokerAPI module documentation:** https://tim55667757.github.io/TKSBrokerAPI/docs/tksbrokerapi/TKSBrokerAPI.html
  8- **TKSBrokerAPI CLI examples:** https://github.com/Tim55667757/TKSBrokerAPI/blob/master/README_EN.md
  9- **About Tinkoff Invest API:** https://tinkoff.github.io/investAPI/
 10- **Tinkoff Invest API documentation:** https://tinkoff.github.io/investAPI/swagger-ui/
 11- **Open account for trading:** http://tinkoff.ru/sl/AaX1Et1omnH
 12"""
 13
 14# Copyright (c) 2022 Gilmillin Timur Mansurovich
 15#
 16# Licensed under the Apache License, Version 2.0 (the "License");
 17# you may not use this file except in compliance with the License.
 18# You may obtain a copy of the License at
 19#
 20#     http://www.apache.org/licenses/LICENSE-2.0
 21#
 22# Unless required by applicable law or agreed to in writing, software
 23# distributed under the License is distributed on an "AS IS" BASIS,
 24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 25# See the License for the specific language governing permissions and
 26# limitations under the License.
 27
 28
 29from datetime import datetime, timedelta
 30from dateutil.tz import tzutc
 31
 32
 33# --- Main constants:
 34
 35NANO = 0.000000001  # SI-constant nano = 10^-9
 36
 37
 38def GetDatesAsString(start: str = None, end: str = None, userFormat: str = "%Y-%m-%d", outputFormat: str = "%Y-%m-%dT%H:%M:%SZ") -> tuple[str, str]:
 39    """
 40    Create tuple of date and time strings with timezone parsed from user-friendly date.
 41
 42    Warning! All dates must be in UTC time zone!
 43
 44    User dates format must be like: `"%Y-%m-%d"`, e.g. `"2020-02-03"` (3 Feb, 2020).
 45
 46    Output date is UTC ISO time format by default: `"%Y-%m-%dT%H:%M:%SZ"`.
 47
 48    Example input: `start="2022-06-01", end="2022-06-20"` -> output: `("2022-06-01T00:00:00Z", "2022-06-20T23:59:59Z")`.
 49    An error exception will occur if input date has incorrect format.
 50
 51    If `start=None`, `end=None` then return dates from yesterday to the end of the day.
 52
 53    If `start=some_date_1`, `end=None` then return dates from `some_date_1` to the end of the day.
 54
 55    If `start=some_date_1`, `end=some_date_2` then return dates from start of `some_date_1` to end of `some_date_2`.
 56
 57    Start day may be negative integer numbers: `-1`, `-2`, `-3` — how many days ago.
 58
 59    Also, you can use keywords for start if `end=None`:
 60    - `today` (from 00:00:00 to the end of current day),
 61    - `yesterday` (-1 day from 00:00:00 to 23:59:59),
 62    - `week` (-7 day from 00:00:00 to the end of current day),
 63    - `month` (-30 day from 00:00:00 to the end of current day),
 64    - `year` (-365 day from 00:00:00 to the end of current day),
 65
 66    :param start: start day in format defined by `userFormat` or keyword.
 67    :param end: end day in format defined by `userFormat`.
 68    :param userFormat: user-friendly date format, e.g. `"%Y-%m-%d"`.
 69    :param outputFormat: output string date format.
 70    :return: tuple with 2 strings `("start", "end")`. Example of return is `("2022-06-01T00:00:00Z", "2022-06-20T23:59:59Z")`.
 71             Second string is the end of the last day.
 72    """
 73    s = datetime.now(tzutc()).replace(hour=0, minute=0, second=0, microsecond=0)  # start of the current day
 74    e = s.replace(hour=23, minute=59, second=59, microsecond=0)  # end of the current day
 75
 76    # time between start and the end of the current day:
 77    if start is None or start.lower() == "today":
 78        pass
 79
 80    # from start of the last day to the end of the last day:
 81    elif start.lower() == "yesterday":
 82        s -= timedelta(days=1)
 83        e -= timedelta(days=1)
 84
 85    # week (-7 day from 00:00:00 to the end of the current day):
 86    elif start.lower() == "week":
 87        s -= timedelta(days=6)  # +1 current day already taken into account
 88
 89    # month (-30 day from 00:00:00 to the end of current day):
 90    elif start.lower() == "month":
 91        s -= timedelta(days=29)  # +1 current day already taken into account
 92
 93    # year (-365 day from 00:00:00 to the end of current day):
 94    elif start.lower() == "year":
 95        s -= timedelta(days=364)  # +1 current day already taken into account
 96
 97    # -N days ago to the end of current day:
 98    elif start.startswith('-') and start[1:].isdigit():
 99        s -= timedelta(days=abs(int(start)) - 1)  # +1 current day already taken into account
100
101    # dates between start day at 00:00:00 and the end of the last day at 23:59:59:
102    else:
103        s = datetime.strptime(start, userFormat).replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=tzutc())
104        e = datetime.strptime(end, userFormat).replace(hour=23, minute=59, second=59, microsecond=0, tzinfo=tzutc()) if end is not None else e
105
106    # converting to UTC ISO time formatted with Z suffix for Tinkoff Open API:
107    s = s.strftime(outputFormat)
108    e = e.strftime(outputFormat)
109
110    return s, e
111
112
113def NanoToFloat(units: str, nano: int) -> float:
114    """
115    Convert number in nano-view mode with string parameter `units` and integer parameter `nano` to float view. Examples:
116
117    `NanoToFloat(units="2", nano=500000000) -> 2.5`
118
119    `NanoToFloat(units="0", nano=50000000) -> 0.05`
120
121    :param units: integer string or integer parameter that represents the integer part of number
122    :param nano: integer string or integer parameter that represents the fractional part of number
123    :return: float view of number
124    """
125    return int(units) + int(nano) * NANO
126
127
128def FloatToNano(number: float) -> dict:
129    """
130    Convert float number to nano-type view: dictionary with string `units` and integer `nano` parameters `{"units": "string", "nano": integer}`. Examples:
131
132    `FloatToNano(number=2.5) -> {"units": "2", "nano": 500000000}`
133
134    `FloatToNano(number=0.05) -> {"units": "0", "nano": 50000000}`
135
136    :param number: float number
137    :return: nano-type view of number: `{"units": "string", "nano": integer}`
138    """
139    splitByPoint = str(number).split(".")
140    frac = 0
141
142    if len(splitByPoint) > 1:
143        if len(splitByPoint[1]) <= 9:
144            frac = int("{}{}".format(
145                int(splitByPoint[1]),
146                "0" * (9 - len(splitByPoint[1])),
147            ))
148
149    if (number < 0) and (frac > 0):
150        frac = -frac
151
152    return {"units": str(int(number)), "nano": frac}
def GetDatesAsString( start: str = None, end: str = None, userFormat: str = '%Y-%m-%d', outputFormat: str = '%Y-%m-%dT%H:%M:%SZ') -> tuple[str, str]:
 39def GetDatesAsString(start: str = None, end: str = None, userFormat: str = "%Y-%m-%d", outputFormat: str = "%Y-%m-%dT%H:%M:%SZ") -> tuple[str, str]:
 40    """
 41    Create tuple of date and time strings with timezone parsed from user-friendly date.
 42
 43    Warning! All dates must be in UTC time zone!
 44
 45    User dates format must be like: `"%Y-%m-%d"`, e.g. `"2020-02-03"` (3 Feb, 2020).
 46
 47    Output date is UTC ISO time format by default: `"%Y-%m-%dT%H:%M:%SZ"`.
 48
 49    Example input: `start="2022-06-01", end="2022-06-20"` -> output: `("2022-06-01T00:00:00Z", "2022-06-20T23:59:59Z")`.
 50    An error exception will occur if input date has incorrect format.
 51
 52    If `start=None`, `end=None` then return dates from yesterday to the end of the day.
 53
 54    If `start=some_date_1`, `end=None` then return dates from `some_date_1` to the end of the day.
 55
 56    If `start=some_date_1`, `end=some_date_2` then return dates from start of `some_date_1` to end of `some_date_2`.
 57
 58    Start day may be negative integer numbers: `-1`, `-2`, `-3` — how many days ago.
 59
 60    Also, you can use keywords for start if `end=None`:
 61    - `today` (from 00:00:00 to the end of current day),
 62    - `yesterday` (-1 day from 00:00:00 to 23:59:59),
 63    - `week` (-7 day from 00:00:00 to the end of current day),
 64    - `month` (-30 day from 00:00:00 to the end of current day),
 65    - `year` (-365 day from 00:00:00 to the end of current day),
 66
 67    :param start: start day in format defined by `userFormat` or keyword.
 68    :param end: end day in format defined by `userFormat`.
 69    :param userFormat: user-friendly date format, e.g. `"%Y-%m-%d"`.
 70    :param outputFormat: output string date format.
 71    :return: tuple with 2 strings `("start", "end")`. Example of return is `("2022-06-01T00:00:00Z", "2022-06-20T23:59:59Z")`.
 72             Second string is the end of the last day.
 73    """
 74    s = datetime.now(tzutc()).replace(hour=0, minute=0, second=0, microsecond=0)  # start of the current day
 75    e = s.replace(hour=23, minute=59, second=59, microsecond=0)  # end of the current day
 76
 77    # time between start and the end of the current day:
 78    if start is None or start.lower() == "today":
 79        pass
 80
 81    # from start of the last day to the end of the last day:
 82    elif start.lower() == "yesterday":
 83        s -= timedelta(days=1)
 84        e -= timedelta(days=1)
 85
 86    # week (-7 day from 00:00:00 to the end of the current day):
 87    elif start.lower() == "week":
 88        s -= timedelta(days=6)  # +1 current day already taken into account
 89
 90    # month (-30 day from 00:00:00 to the end of current day):
 91    elif start.lower() == "month":
 92        s -= timedelta(days=29)  # +1 current day already taken into account
 93
 94    # year (-365 day from 00:00:00 to the end of current day):
 95    elif start.lower() == "year":
 96        s -= timedelta(days=364)  # +1 current day already taken into account
 97
 98    # -N days ago to the end of current day:
 99    elif start.startswith('-') and start[1:].isdigit():
100        s -= timedelta(days=abs(int(start)) - 1)  # +1 current day already taken into account
101
102    # dates between start day at 00:00:00 and the end of the last day at 23:59:59:
103    else:
104        s = datetime.strptime(start, userFormat).replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=tzutc())
105        e = datetime.strptime(end, userFormat).replace(hour=23, minute=59, second=59, microsecond=0, tzinfo=tzutc()) if end is not None else e
106
107    # converting to UTC ISO time formatted with Z suffix for Tinkoff Open API:
108    s = s.strftime(outputFormat)
109    e = e.strftime(outputFormat)
110
111    return s, e

Create tuple of date and time strings with timezone parsed from user-friendly date.

Warning! All dates must be in UTC time zone!

User dates format must be like: "%Y-%m-%d", e.g. "2020-02-03" (3 Feb, 2020).

Output date is UTC ISO time format by default: "%Y-%m-%dT%H:%M:%SZ".

Example input: start="2022-06-01", end="2022-06-20" -> output: ("2022-06-01T00:00:00Z", "2022-06-20T23:59:59Z"). An error exception will occur if input date has incorrect format.

If start=None, end=None then return dates from yesterday to the end of the day.

If start=some_date_1, end=None then return dates from some_date_1 to the end of the day.

If start=some_date_1, end=some_date_2 then return dates from start of some_date_1 to end of some_date_2.

Start day may be negative integer numbers: -1, -2, -3 — how many days ago.

Also, you can use keywords for start if end=None:

  • today (from 00:00:00 to the end of current day),
  • yesterday (-1 day from 00:00:00 to 23:59:59),
  • week (-7 day from 00:00:00 to the end of current day),
  • month (-30 day from 00:00:00 to the end of current day),
  • year (-365 day from 00:00:00 to the end of current day),
Parameters
  • start: start day in format defined by userFormat or keyword.
  • end: end day in format defined by userFormat.
  • userFormat: user-friendly date format, e.g. "%Y-%m-%d".
  • outputFormat: output string date format.
Returns

tuple with 2 strings ("start", "end"). Example of return is ("2022-06-01T00:00:00Z", "2022-06-20T23:59:59Z"). Second string is the end of the last day.

def NanoToFloat(units: str, nano: int) -> float:
114def NanoToFloat(units: str, nano: int) -> float:
115    """
116    Convert number in nano-view mode with string parameter `units` and integer parameter `nano` to float view. Examples:
117
118    `NanoToFloat(units="2", nano=500000000) -> 2.5`
119
120    `NanoToFloat(units="0", nano=50000000) -> 0.05`
121
122    :param units: integer string or integer parameter that represents the integer part of number
123    :param nano: integer string or integer parameter that represents the fractional part of number
124    :return: float view of number
125    """
126    return int(units) + int(nano) * NANO

Convert number in nano-view mode with string parameter units and integer parameter nano to float view. Examples:

NanoToFloat(units="2", nano=500000000) -> 2.5

NanoToFloat(units="0", nano=50000000) -> 0.05

Parameters
  • units: integer string or integer parameter that represents the integer part of number
  • nano: integer string or integer parameter that represents the fractional part of number
Returns

float view of number

def FloatToNano(number: float) -> dict:
129def FloatToNano(number: float) -> dict:
130    """
131    Convert float number to nano-type view: dictionary with string `units` and integer `nano` parameters `{"units": "string", "nano": integer}`. Examples:
132
133    `FloatToNano(number=2.5) -> {"units": "2", "nano": 500000000}`
134
135    `FloatToNano(number=0.05) -> {"units": "0", "nano": 50000000}`
136
137    :param number: float number
138    :return: nano-type view of number: `{"units": "string", "nano": integer}`
139    """
140    splitByPoint = str(number).split(".")
141    frac = 0
142
143    if len(splitByPoint) > 1:
144        if len(splitByPoint[1]) <= 9:
145            frac = int("{}{}".format(
146                int(splitByPoint[1]),
147                "0" * (9 - len(splitByPoint[1])),
148            ))
149
150    if (number < 0) and (frac > 0):
151        frac = -frac
152
153    return {"units": str(int(number)), "nano": frac}

Convert float number to nano-type view: dictionary with string units and integer nano parameters {"units": "string", "nano": integer}. Examples:

FloatToNano(number=2.5) -> {"units": "2", "nano": 500000000}

FloatToNano(number=0.05) -> {"units": "0", "nano": 50000000}

Parameters
  • number: float number
Returns

nano-type view of number: {"units": "string", "nano": integer}