forked from robcarver17/python-uk-trading-tax-calculator
-
Notifications
You must be signed in to change notification settings - Fork 6
/
positions.py
129 lines (82 loc) · 2.84 KB
/
positions.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
"""
Python UK trading tax calculator
Copyright (C) 2015 Robert Carver
You may copy, modify and redistribute this file as allowed in the license agreement
but you must retain this header
See README.txt
"""
import pandas as pd
from utils import type_and_sense_check_arguments
from trades import THRESHOLD
class Position(object):
"""
A position object contains our current position in something
"""
def _possible_args(self):
return self._required_columns()+self._optional_columns()
def _required_columns(self):
return ['Code', 'Position']
def _optional_columns(self):
return []
def _type_check(self):
arg_types=dict(Code=str, Position=float)
return arg_types
def __init__(self, **kwargs):
'''
Constructor
'''
type_and_sense_check_arguments(self, kwargs)
argsused=[]
for key in kwargs:
argsused.append(key)
setattr(self, key, kwargs[key])
setattr(self, 'argsused', argsused)
def modify(self, **kwargs):
modpositions=type_and_sense_check_arguments(self, kwargs, checkrequired=False)
argsused=self.argsused
for key in modpositions:
setattr(self, key, modpositions[key])
argsused.append(key)
argsused=list(set(argsused))
setattr(self, 'argsused', argsused)
def __repr__(self):
ans=", ".join(["%s:%s" % (name, str(getattr(self, name))) for name in self.argsused])
return ans
class PositionList(list):
'''
A PositionList object is a list of positions
'''
def as_dict(self):
ans=dict([(x.Code, x.Position) for x in self])
return ans
def list_breaks(dict1, dict2):
"""
Returns a dataframe of breaks
"""
results=compare_position_dicts(dict1, dict2)
ans=results[results.Break==True]
ans.sort_values("Code")
return ans
def not_matching_position(x, y):
if abs(x-y)>THRESHOLD:
return False
def compare_position_dicts(dict1, dict2):
"""
Compare two position dicts to see if any break
"""
codes1=dict1.keys()
codes2=dict2.keys()
joint_codes=list(set(list(codes1)+list(codes2)))
pos1=[dict1.get(code,0) for code in joint_codes]
pos2=[dict2.get(code,0) for code in joint_codes]
any_break=[not_matching_position(pos1[idx], pos2[idx]) for idx in range(len(joint_codes))]
results=pd.DataFrame(dict(Code=joint_codes, Position1=pos1, Position2=pos2, Break=any_break))
return results
def compare_trades_and_positions(all_trades, all_positions):
"""
Compares the final positions imputed from a list of trades, and a list of positions.
Good sanity check
"""
posdict1=all_trades.final_positions_as_dict()
posdict2=all_positions.as_dict()
return list_breaks(posdict1, posdict2)