import yaml
import csv
def parse_input(input_data):
""" Parse input string into structured dictionary. """
portfolio = {}
for line in input_data:
parts = line.split(';')
instrument, weight, categories = parts[0], float(parts[1]), parts[2:]
# Navigate and create dictionary structure
current_level = portfolio
for category in categories:
if category not in current_level:
current_level[category] = {}
current_level = current_level[category]
current_level[instrument] = weight
return portfolio
def sum_weights(data):
""" Recursively sum weights to calculate total weights for categories. """
if isinstance(data, dict):
return sum(sum_weights(value) for value in data.values())
return data
def display_weights(data, level=0):
global current_category
if isinstance(data, dict):
total_weight = sum_weights(data)
formatted_weight = f"{total_weight * 100:.2f}%" # Convert to percentage and format
print(' ' * (level * 4) + f'{current_category}: {formatted_weight}')
for key, value in data.items():
current_category = key # Update the current category
display_weights(value, level + 1)
else:
formatted_weight = f"{data * 100:.2f}%" # Convert to percentage and format
print(' ' * (level * 4) + f'{current_category} (Instrument): {formatted_weight}')
def adjust_weights(data, category_total_weight, percent_change, add=True):
""" Adjust weights for a specific category by a percentage based on proportional contributions. """
adjustment_ratio = percent_change / 100 # Convert percentage to decimal
def adjust_recursive(sub_data, category_total_weight):
""" Recursively adjust weights in a nested dictionary. """
if isinstance(sub_data, dict):
for key, value in sub_data.items():
if isinstance(value, dict):
adjust_recursive(value, category_total_weight)
else:
# Calculate the proportion of the total category weight that this instrument represents
proportion_of_category = value / category_total_weight
# Calculate the absolute amount to adjust this instrument by
adjustment_amount = proportion_of_category * adjustment_ratio
if add:
sub_data[key] = value + adjustment_amount
else:
sub_data[key] = value - adjustment_amount
# Initiate recursive adjustment
adjust_recursive(data, category_total_weight)
def rebalance_portfolio(portfolio, source_category, target_category, percent_change):
if source_category in portfolio and target_category in portfolio:
source_category_total_weight = sum_weights(portfolio[source_category])
target_category_total_weight = sum_weights(portfolio[target_category])
# Deduct weight proportionally from the source category
adjust_weights(portfolio[source_category], source_category_total_weight, percent_change, add=False)
# Add weight proportionally to the target category
adjust_weights(portfolio[target_category], target_category_total_weight, percent_change, add=True)
print(f"\nUpdated portfolio weights after transferring {percent_change}% from {source_category} to {target_category}:")
display_weights(portfolio)
else:
print("One or both categories not found in the portfolio.")
def main_menu(portfolio):
while True:
print("\nMenu:")
print("1 - Display All Weights")
print("2 - Rebalance Portfolio Weights")
print("3 - Exit")
choice = input("Enter your choice: ")
if choice == '1':
current_category = "Portfolio"
display_weights(portfolio)
elif choice == '2':
source_category = input("Enter the source category to deduct weight from: ")
target_category = input("Enter the target category to add weight to: ")
percent_change = float(input("Enter the percentage to move (e.g., 5 for 5%): "))
rebalance_portfolio(portfolio, source_category, target_category, percent_change)
elif choice == '3':
print("Exiting program.")
break
else:
print("Invalid choice, please try again.")
def load_yaml_data(filepath):
""" Load data from a YAML file. """
with open(filepath, 'r') as file:
return yaml.safe_load(file)
def load_csv_data(filepath):
""" Load data from a CSV file. """
data = {}
with open(filepath, newline='') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
data[row['Instrument']] = {
'AssetClass': row['AssetClass'],
'SubClass': row['SubClass'],
'SubSubClass': row['SubSubClass'],
'Country': row['Country']
}
return data
def build_input_data(weights_config, instruments_info):
""" Build input data combining weights and asset class info. """
input_data = []
for instrument, weight in weights_config.get('instrument_weights', {}).items():
if instrument in instruments_info:
info = instruments_info[instrument]
path = [instrument, str(weight), info['AssetClass'], info['SubClass']]
if info['SubSubClass']:
path.append(info['SubSubClass'])
if info['Country']:
path.append(info['Country'])
input_data.append(";".join(path))
return input_data
# Load the data from files
config_data = load_yaml_data('PATH TO YOUR config.yaml WITH INSTRUMENT WEIGHT')
instrument_info = load_csv_data('PATH TO moreinstrumentinfo.csv NORMALLY UNDER data.futures.csvconfig')
# Build the input data
input_data = build_input_data(config_data, instrument_info)
print(input_data)
# Parsing the input
portfolio = parse_input(input_data)
# Display weights
current_category = "Portfolio"
# Start menu
main_menu(portfolio)