Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
517 changes: 517 additions & 0 deletions REFACTOR_PLAN.md

Large diffs are not rendered by default.

341 changes: 114 additions & 227 deletions scripts/calc.py

Large diffs are not rendered by default.

606 changes: 210 additions & 396 deletions scripts/classes.py

Large diffs are not rendered by default.

117 changes: 117 additions & 0 deletions scripts/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""
Centralized constants for the portfolio simulation system.

This module is the single source of truth for multipliers, contract months,
month/symbol mappings, tick sizes, and other constants that were previously
duplicated across multiple files.
"""

from typing import NamedTuple, Dict
from enum import Enum


class ProductSpec(NamedTuple):
dollar_mult: float
lot_mult: float
futures_tick: float
options_tick: float
pnl_mult: float


multipliers: Dict[str, ProductSpec] = {
'LH': ProductSpec(22.046, 18.143881, 0.025, 1, 400),
'LSU': ProductSpec(1, 50, 0.1, 10, 50),
'QC': ProductSpec(1.2153, 10, 1, 25, 12.153),
'SB': ProductSpec(22.046, 50.802867, 0.01, 0.25, 1120),
'CC': ProductSpec(1, 10, 1, 50, 10),
'CT': ProductSpec(22.046, 22.679851, 0.01, 1, 500),
'KC': ProductSpec(22.046, 17.009888, 0.05, 2.5, 375),
'W': ProductSpec(0.3674333, 136.07911, 0.25, 10, 50),
'S': ProductSpec(0.3674333, 136.07911, 0.25, 10, 50),
'C': ProductSpec(0.393678571428571, 127.007166832986, 0.25, 10, 50),
'BO': ProductSpec(22.046, 27.215821, 0.01, 0.5, 600),
'LC': ProductSpec(22.046, 18.143881, 0.025, 1, 400),
'LRC': ProductSpec(1, 10, 1, 50, 10),
'KW': ProductSpec(0.3674333, 136.07911, 0.25, 10, 50),
'SM': ProductSpec(1.1023113, 90.718447, 0.1, 5, 100),
'COM': ProductSpec(1.0604, 50, 0.25, 2.5, 53.02),
'CA': ProductSpec(1.0604, 50, 0.25, 1, 53.02),
'MW': ProductSpec(0.3674333, 136.07911, 0.25, 10, 50),
}

# Dictionary mapping month number to symbol and vice versa
month_to_sym = {1: 'F', 2: 'G', 3: 'H', 4: 'J', 5: 'K', 6: 'M',
7: 'N', 8: 'Q', 9: 'U', 10: 'V', 11: 'X', 12: 'Z'}
sym_to_month = {'F': 1, 'G': 2, 'H': 3, 'J': 4, 'K': 5,
'M': 6, 'N': 7, 'Q': 8, 'U': 9, 'V': 10, 'X': 11, 'Z': 12}

# Contract months for each commodity
# NOTE: LC previously had a missing-comma bug ('V' 'Z' -> 'VZ'). Fixed here.
contract_mths = {
'LH': ['G', 'J', 'K', 'M', 'N', 'Q', 'V', 'Z'],
'LSU': ['H', 'K', 'Q', 'V', 'Z'],
'QC': ['H', 'K', 'N', 'U', 'Z'],
'SB': ['H', 'K', 'N', 'V'],
'CC': ['H', 'K', 'N', 'U', 'Z'],
'CT': ['H', 'K', 'N', 'Z'],
'KC': ['H', 'K', 'N', 'U', 'Z'],
'W': ['H', 'K', 'N', 'U', 'Z'],
'S': ['F', 'H', 'K', 'N', 'Q', 'U', 'X'],
'C': ['H', 'K', 'N', 'U', 'Z'],
'BO': ['F', 'H', 'K', 'N', 'Q', 'U', 'V', 'Z'],
'LC': ['G', 'J', 'M', 'Q', 'V', 'Z'],
'LRC': ['F', 'H', 'K', 'N', 'U', 'X'],
'KW': ['H', 'K', 'N', 'U', 'Z'],
'SM': ['F', 'H', 'K', 'N', 'Q', 'U', 'V', 'Z'],
'COM': ['G', 'K', 'Q', 'X'],
'CA': ['H', 'K', 'U', 'Z'],
'MW': ['H', 'K', 'N', 'U', 'Z'],
}

# Options tick sizes (used for hedging strike selection)
op_ticksize = {
'QC': 1,
'CC': 1,
'SB': 0.01,
'LSU': 0.05,
'KC': 0.01,
'DF': 1,
'CT': 0.01,
'C': 0.125,
'S': 0.125,
'SM': 0.05,
'BO': 0.005,
'W': 0.125,
'MW': 0.125,
'KW': 0.125,
}

RANDOM_SEED = 7
DECADE = 10
TIMESTEP = 1 / 365
BREAKEVEN_FACTOR = 2.8


class OptionType(str, Enum):
CALL = 'call'
PUT = 'put'


class BarrierStyle(str, Enum):
AMERICAN = 'amer'
EUROPEAN = 'euro'


class BarrierDirection(str, Enum):
UP = 'up'
DOWN = 'down'


class SecurityType(str, Enum):
OPTION = 'option'
FUTURE = 'future'


class PositionFlag(str, Enum):
OTC = 'OTC'
HEDGE = 'hedge'
30 changes: 5 additions & 25 deletions scripts/fetch_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,7 @@
from joblib import Parallel, delayed
import getpass

contract_mths = {

'LH': ['G', 'J', 'K', 'M', 'N', 'Q', 'V', 'Z'],
'LSU': ['H', 'K', 'Q', 'V', 'Z'],
'QC': ['H', 'K', 'N', 'U', 'Z'],
'SB': ['H', 'K', 'N', 'V'],
'CC': ['H', 'K', 'N', 'U', 'Z'],
'CT': ['H', 'K', 'N', 'V', 'Z'],
'KC': ['H', 'K', 'N', 'U', 'Z'],
'W': ['H', 'K', 'N', 'U', 'Z'],
'S': ['F', 'H', 'K', 'N', 'Q', 'U', 'X'],
'C': ['H', 'K', 'N', 'U', 'Z'],
'BO': ['F', 'H', 'K', 'N', 'Q', 'U', 'V', 'Z'],
'LC': ['G', 'J', 'M', 'Q', 'V' 'Z'],
'LRC': ['F', 'H', 'K', 'N', 'U', 'X'],
'KW': ['H', 'K', 'N', 'U', 'Z'],
'SM': ['F', 'H', 'K', 'N', 'Q', 'U', 'V', 'Z'],
'COM': ['G', 'K', 'Q', 'X'],
'CA': ['H', 'K', 'U', 'Z'],
'MW': ['H', 'K', 'N', 'U', 'Z']
}
from .constants import contract_mths


def pull_settlement_data(pdt, start_date=None, end_date=None, write_dump=False,
Expand All @@ -59,7 +39,7 @@ def pull_settlement_data(pdt, start_date=None, end_date=None, write_dump=False,
assert (start_date is not None or end_date is not None) or write_dump

print('starting clock..')
t = time.clock()
t = time.perf_counter()

user = getpass.getuser()
password = getpass.getpass()
Expand Down Expand Up @@ -94,7 +74,7 @@ def pull_settlement_data(pdt, start_date=None, end_date=None, write_dump=False,
# '_raw_data.csv', index=False)

print('finished pulling data')
print('elapsed: ', time.clock() - t)
print('elapsed: ', time.perf_counter() - t)

add = 1 if len(pdt) == 2 else 0

Expand Down Expand Up @@ -554,7 +534,7 @@ def pull_intraday_data(pdts, start_date=None, end_date=None, filepath='', contra
"""
overnight_pdts = {'BO', 'C', 'KW', 'S', 'SM', 'W', 'CT', 'MW'}

t = time.clock()
t = time.perf_counter()

par = True if len(pdts) > 1 else False

Expand All @@ -568,7 +548,7 @@ def pull_intraday_data(pdts, start_date=None, end_date=None, filepath='', contra
overnight_pdts=overnight_pdts) for pdt in pdts)
df = pd.concat(res)

print('elapsed: ', time.clock() - t)
print('elapsed: ', time.perf_counter() - t)

return df

Expand Down
54 changes: 8 additions & 46 deletions scripts/hedge.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,7 @@
from .util import create_straddle, create_underlying, create_strangle, create_vanilla_option
from .calc import _compute_value
from .hedge_mods import TrailingStop, HedgeParser

multipliers = {
'LH': [22.046, 18.143881, 0.025, 1, 400],
'LSU': [1, 50, 0.1, 10, 50],
'QC': [1.2153, 10, 1, 25, 12.153],
'SB': [22.046, 50.802867, 0.01, 0.25, 1120],
'CC': [1, 10, 1, 50, 10],
'CT': [22.046, 22.679851, 0.01, 1, 500],
'KC': [22.046, 17.009888, 0.05, 2.5, 375],
'W': [0.3674333, 136.07911, 0.25, 10, 50],
'S': [0.3674333, 136.07911, 0.25, 10, 50],
'C': [0.393678571428571, 127.007166832986, 0.25, 10, 50],
'BO': [22.046, 27.215821, 0.01, 0.5, 600],
'LC': [22.046, 18.143881, 0.025, 1, 400],
'LRC': [1, 10, 1, 50, 10],
'KW': [0.3674333, 136.07911, 0.25, 10, 50],
'SM': [1.1023113, 90.718447, 0.1, 5, 100],
'COM': [1.0604, 50, 0.25, 2.5, 53.02],
'CA': [1.0604, 50, 0.25, 1, 53.02],
'MW': [0.3674333, 136.07911, 0.25, 10, 50]
}

op_ticksize = {

'QC': 1,
'CC': 1,
'SB': 0.01,
'LSU': 0.05,
'KC': 0.01,
'DF': 1,
'CT': 0.01,
'C': 0.125,
'S': 0.125,
'SM': 0.05,
'BO': 0.005,
'W': 0.125,
'MW': 0.125,
'KW': 0.125
}
from .constants import multipliers, op_ticksize


class Hedge:
Expand Down Expand Up @@ -718,21 +680,21 @@ def hedge(self, flag, product, loc, greekval, target):
if self.book:
for op in ops:
try:
cpi = 'C' if op.char == 'call' else 'P'
cpi = 'C' if op.option_type == 'call' else 'P'
df = self.settlements
settle_vol = df[(df.vol_id == op.get_vol_id()) &
(df.call_put_id == cpi) &
(df.strike == op.K)].vol.values[0]
(df.strike == op.strike)].vol.values[0]
except IndexError as e:
print('scripts.hedge - book vol case: cannot find vol: ',
op.get_vol_id(), cpi, op.K)
op.get_vol_id(), cpi, op.strike)
settle_vol = op.vol
# print(op.get_vol_id() + ' settle_vol: ', settle_vol)
# print('op.book vol: ', op.vol)
true_value = _compute_value(op.char, op.tau, settle_vol, op.K,
op.underlying.get_price(), 0, 'amer', ki=op.ki,
ko=op.ko, barrier=op.barrier, d=op.direc,
product=op.get_product(), bvol=op.bvol)
true_value = _compute_value(op.option_type, op.tau, settle_vol, op.strike,
op.underlying.get_price(), 0, 'amer', knock_in=op.knock_in,
knock_out=op.knock_out, barrier=op.barrier, d=op.direction,
product=op.get_product(), barrier_vol=op.barrier_vol)
# print('op value basis settlements: ', true_value)
pnl_mult = multipliers[op.get_product()][-1]
diff = (true_value - op.get_price()) * op.lots * pnl_mult
Expand Down
Loading