Skip to content

Commit 8a21991

Browse files
committed
added calendar functionality
1 parent 19ba217 commit 8a21991

File tree

5 files changed

+72
-10
lines changed

5 files changed

+72
-10
lines changed

margot/backtest/backtest.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ def calculate_returns(self):
4242
You should construct your MargotDataFrame to be indexed by the trading
4343
periods (e.g. days).
4444
"""
45-
#TODO Remove the assumption about log_returns
45+
#TODO Remove the assumption about log_returns. i reckon look for the
46+
# column, then derive simple then log returns.
4647

4748
returns = self.positions.shift()
4849
for column in returns.columns:
@@ -116,4 +117,4 @@ def run(self, periods=None):
116117
# caclulate lookback rolling volatility
117118
# simulate resizing at trading time, according to a target volatility
118119
# simulate volatility sized returns.
119-
return self.positions
120+
return self.returns

margot/signals/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from margot.signals.algos import BaseAlgo
22

3-
43
class Position(object):
54
"""Represents a Position with a symbol and a weight.
65
@@ -10,9 +9,10 @@ class Position(object):
109
the weight of this symbol in the position list.
1110
"""
1211

13-
def __init__(self, symbol: str, weight: float): # noqa: D107
12+
def __init__(self, symbol: str, weight: float, order_type: str): # noqa: D107
1413
self.symbol = symbol
1514
self.weight = weight
15+
# TODO order_type should be checked.
1616
if self.weight > 1.0 or self.weight < -1.0:
1717
raise ValueError('weight must be a value between -1.0 and +1.0')
1818

margot/signals/algos.py

+64-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from trading_calendars import get_calendar
1111

1212
import pytz
13-
from margot.signals.periods import DAILY
13+
14+
from margot.signals.order_types import MOC, MKT, LMT
1415
from margot.data import MargotDataFrame
1516

1617

@@ -22,7 +23,8 @@ class BaseAlgo(object):
2223
2324
Args:
2425
env (dict): a dictionary of environment variables, e.g. API keys.
25-
Overrides anything provided in sys env.
26+
Overrides anything provided in sys env.
27+
market (str): The ISO code for the market we will use.
2628
2729
Raises:
2830
ValueError: the attribute, 'data' must be a reference to a MargotDataFrame.
@@ -31,15 +33,71 @@ class BaseAlgo(object):
3133
3234
"""
3335

34-
frequency = DAILY
36+
MOC = MOC
37+
MKT = MKT
38+
LMT = LMT
39+
40+
MONDAY = 'MON'
41+
TUESDAY = 'TUE'
42+
WEDNESDAY = 'WED'
43+
THURSDAY = 'THU'
44+
FRIDAY = 'FRI'
45+
SATURDAY = 'SAT'
46+
SUNDAY = 'SUN'
47+
3548
data = None
3649

37-
def __init__(self, env: dict = {}, calendar='XNYS'): # noqa: D107
50+
def __init__(self, env: dict = {}, market='XNYS'): # noqa: D107
3851
self.env = env
39-
self.calendar = calendar
52+
self.market = get_calendar(market)
53+
self.when = None
4054
if not isinstance(self.data, MargotDataFrame):
4155
raise ValueError('Please set data to reference a MargotDataFrame')
4256

57+
def weekday(self, dt):
58+
"""Return a human readable three letter day of week.
59+
60+
Convert the Python integer representation of day of week into a string.
61+
62+
e.g::
63+
64+
0: 'MON' (also known as self.MONDAY)
65+
66+
.. note::
67+
You should always use the built in constants when passing days of
68+
the week. e.g. self.MONDAY, self.TUESDAY, ... these map to the three
69+
charater strings.
70+
71+
72+
Args:
73+
dt (datetime or pd.Timestamp): The datetime to check
74+
75+
Returns:
76+
str: One of; 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'
77+
"""
78+
days = {
79+
0: self.MONDAY,
80+
1: self.TUESDAY,
81+
2: self.WEDNESDAY,
82+
3: self.THURSDAY,
83+
4: self.FRIDAY,
84+
5: self.SATURDAY,
85+
6: self.SUNDAY
86+
}
87+
return days.get(dt.weekday())
88+
89+
@property
90+
def next_close(self):
91+
"""Return a UTC pd.Timestamp of the next close of trading session.
92+
93+
Returns:
94+
pd.Timestamp: Timestamp of the next close of cash session in UTC.
95+
"""
96+
return self.market.next_close(
97+
getattr(self, 'when',
98+
pd.Timestamp(datetime.now(tz=pytz.UTC)))
99+
)
100+
43101
def signal(self) -> list:
44102
"""Return a list of Position objects for a given datetime."""
45103
raise NotImplementedError("You must implement signal")
@@ -61,6 +119,7 @@ def simulate_signal(self, when: datetime):
61119
list: a list of Position objects.
62120
"""
63121
self.data.simulate(when)
122+
self.when = when
64123
positions = self.signal()
65124
self.data.end_simulation()
66125
return positions

margot/signals/order_types.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
MOC = 'Market On Close'
2+
MKT = 'Market'
3+
LMT = 'Limit'

todo.md

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
* pairs trading
1010
* decouple positions + returns from rebalancing
11-
* add 3DMA filter back to the vix basis
1211
* risk management
1312
* live trading
1413
* accounting

0 commit comments

Comments
 (0)