Skip to content

Commit

Permalink
multiple improvements that were never committed
Browse files Browse the repository at this point in the history
- improved model startup
	- defined constants
	- randomized starting point
- better fitness tracking
- improved simulation
  • Loading branch information
Jrc356 committed Jan 20, 2020
1 parent 002b694 commit 07d692c
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 84 deletions.
55 changes: 28 additions & 27 deletions bot/config
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,48 @@

[NEAT]
fitness_criterion = max
fitness_threshold = 1000
no_fitness_termination = 1
pop_size = 5
fitness_threshold = 0.2
pop_size = 2
reset_on_extinction = 0

[DefaultGenome]
num_inputs = 576
num_hidden = 1
num_inputs = 412
num_hidden = 200
num_outputs = 2
feed_forward = False
compatibility_disjoint_coefficient = 1.0
compatibility_weight_coefficient = 0.6
initial_connection = full_nodirect
# structural_mutation_surer = 1
conn_add_prob = 0.2
conn_delete_prob = 0.2
node_add_prob = 0.2
node_delete_prob = 0.2
activation_default = sigmoid
activation_options = sigmoid
activation_mutate_rate = 0.1
activation_default = relu
activation_options = relu sigmoid softplus
activation_mutate_rate = 0.2
aggregation_default = sum
aggregation_options = sum
aggregation_mutate_rate = 0.1
bias_init_mean = 0.5
bias_init_stdev = 0.1
aggregation_mutate_rate = 0.2
bias_init_mean = 0
bias_init_stdev = 1
bias_replace_rate = 0.1
bias_mutate_rate = 0.7
bias_mutate_power = 0.5
bias_max_value = 1
bias_min_value = 0
response_init_mean = 0.5
response_init_stdev = 0.1
response_replace_rate = 0.0
response_mutate_rate = 0.0
response_mutate_power = 0.0
response_max_value = 1
response_min_value = 0
bias_max_value = 100
bias_min_value = -100
response_init_mean = 0
response_init_stdev = 1
response_replace_rate = 0.3
response_mutate_rate = 0.3
response_mutate_power = 0.2
response_max_value = 100
response_min_value = -100

weight_max_value = 1
weight_min_value = 0
weight_init_mean = 0.5
weight_init_stdev = 0.1
weight_max_value = 100
weight_min_value = -100
weight_init_mean = 0
weight_init_stdev = 1
weight_mutate_rate = 0.8
weight_replace_rate = 0.1
weight_mutate_power = 0.5
Expand All @@ -57,8 +58,8 @@ compatibility_threshold = 3.0

[DefaultStagnation]
species_fitness_func = max
max_stagnation = 20
max_stagnation = 10

[DefaultReproduction]
elitism = 2
survival_threshold = 0.2
elitism = 0
survival_threshold = 0.25
31 changes: 22 additions & 9 deletions bot/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,50 @@
import pickle
import simulation
import visualize
import random

runs_per_net = 3
simulation_minutes = 60 * 1 # 24 hours in minutes
RUNS_PER_NET = 1
START_BALANCE = 1000
SIM_MINUTES = 60 * 6
STARTING_POINT = random.random()
while STARTING_POINT < 0.2 or STARTING_POINT > 0.8:
STARTING_POINT = random.random()
print(f"STARTING_POINT value: {STARTING_POINT}")

seed = random.randint(0, 1000)
print(f"SEED: {seed}")
random.seed(seed)

pool_size = multiprocessing.cpu_count() - 1
print(f"POOL SIZE: {pool_size}")


def eval_genome(genome, config):
net = neat.nn.RecurrentNetwork.create(genome, config)

fitnesses = []

for runs in range(runs_per_net):
sim = simulation.MarketSim(simulation_minutes)
for runs in range(RUNS_PER_NET):
sim = simulation.MarketSim(SIM_MINUTES, STARTING_POINT, START_BALANCE)
net.reset()

fitness = sim.balance
fitness = 0.00

while not sim.done:
inputs = sim.getState()
action = net.activate(inputs)
trade = simulation.getTrade(action)
sim.step(trade)

if sim.balance <= 500 and sim.holding == 0:
if sim.balance <= (START_BALANCE/2) and sim.holding == 0:
break

fitness = sim.balance
fitness = sim.getFitness()

fitnesses.append(fitness)

return min(fitnesses)
worst = min(fitnesses)
return worst


def eval_genomes(genomes, config):
Expand Down Expand Up @@ -71,6 +85,5 @@ def run():
visualize.draw_net(config, winner, True, node_names=node_names)



if __name__ == '__main__':
run()
106 changes: 76 additions & 30 deletions bot/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,79 +9,125 @@
CONFIG = json.load(f)


def getPairFiles():
for pair in CONFIG["PAIRS"]:
pair_file = join(DATASETS_PATH, pair, f"{pair}-COMPLETE.ftr")
yield pair_file


class MarketSim(object):
def __init__(self, sim_length_in_minutes):
__PENALTY = 0.001

def __init__(self, sim_length_in_minutes, starting_point, starting_balance=1000, pair="BTCUSD"):
self.sim_length = sim_length_in_minutes
self.balance = 1000.00 # in dollars
self.balance = starting_balance # in dollars
self.starting_balance = starting_balance
self.pair = pair
self.pair_file = join(DATASETS_PATH, pair, f"{pair}-COMPLETE.ftr")
self.holding = 0.00 # total coin held
self.__ts_index = 0

self.__counter = 0
self.__buy_counter = 0
self.__penalty_mult = 1
self.__penalty_time = int(self.sim_length * 0.05)

max_idx = len(pd.read_feather(self.pair_file)) - self.sim_length
self.__ts_index = int(starting_point * max_idx)
self.__end_ts_index = self.__ts_index + self.sim_length
self.__timestamp = self.__getTimeStamp()
self.done = False

first_pair = next(getPairFiles())
df = pd.read_feather(first_pair)
self.__end_timestamp = pd.read_feather(first_pair).iloc[len(df) - 1, 0]
df = pd.read_feather(self.pair_file)
self.__max_timestamp = pd.read_feather(self.pair_file).iloc[len(df) - 1, 0]
self.__sim_end_timestamp = pd.read_feather(self.pair_file).iloc[self.__end_ts_index, 0]

# print(f"STARTING SIMULATION FROM {self.__timestamp} to {self.__sim_end_timestamp}")

def __getTimeStamp(self):
return pd.read_feather(next(getPairFiles())).iloc[self.__ts_index, 0]
return pd.read_feather(self.pair_file).iloc[self.__ts_index, 0]

def buy(self, pct_balance):
pct_balance = round(pct_balance, 2)
if self.balance == 0 or pct_balance < 0.5:
return

if pct_balance > 1:
pct_balance = 1

spend_amt = pct_balance * self.balance
cost = next(getPairFiles()).iloc[self.__ts_index, ].CLOSE
cost = pd.read_feather(self.pair_file).iloc[self.__ts_index, ].CLOSE

purchase_amt = spend_amt/cost
purchase_amt = round(purchase_amt, 8) # round to satoshis
# print(f"BOUGHT {pct_balance * 100:.2f}% OF BALANCE ${self.balance:.2f} OR {purchase_amt} {self.pair} FOR {spend_amt:.2f} ON {self.__timestamp}!")
self.holding += purchase_amt
self.balance -= spend_amt

self.__buy_counter += 1

def sell(self, pct_holding):
if self.holding == 0 or pct_holding < 0.5:
return

if pct_holding > 1:
pct_holding = 1

sell_amt = pct_holding * self.holding
cost = pd.read_feather(next(getPairFiles())).iloc[self.__ts_index, ].CLOSE
cost = pd.read_feather(self.pair_file).iloc[self.__ts_index, ].CLOSE

earnings = sell_amt * cost
# print(f"SELLING {pct_holding * 100:.2f}% OF HOLDING {self.holding:.2f} OR {sell_amt} {self.pair} FOR ${earnings:.2f} ON {self.__timestamp}!")
self.holding -= sell_amt
self.balance += earnings

def step(self, trade):
self.__counter += 1
self.__ts_index += 1
self.__timestamp = self.__getTimeStamp()

if trade[0] == "SELL":
# print(f"selling {trade[1]}% of holdings")
self.sell(trade[1])
elif trade[0] == "BUY":
# print(f"buying {trade[1]}% worth of balance")
self.buy(trade[1])
else:
# print("Holding")
pass

if self.__timestamp == self.__end_timestamp or self.__ts_index >= self.sim_length:
if self.__timestamp == self.__max_timestamp or self.__ts_index >= self.__end_ts_index:
self.done = True

def getState(self):
dat = []
for pair in getPairFiles():
df = pd.read_feather(pair)
df = df[df.TS == self.__timestamp]

values = df.drop("TS", axis=1).values.tolist()
if len(values) == 0:
values = [[0 for _ in range(len(df.columns) - 1)]] # without ts

dat.extend(values[0])


df = pd.read_feather(self.pair_file)
df = df.iloc[self.__ts_index - 10:self.__ts_index]

values = df.drop("TS", axis=1).values.tolist()
for v in values:
dat.extend(v)

# for pair in getPairFiles():
# df = pd.read_feather(pair)
# df = df[df.TS == self.__timestamp]

# values = df.drop("TS", axis=1).values.tolist()
# if len(values) == 0:
# values = [[0 for _ in range(len(df.columns) - 1)]] # without ts

# dat.extend(values[0])

dat.extend([self.holding, self.balance])

return dat

def getHoldingsValue(self):
cost = pd.read_feather(self.pair_file).iloc[self.__ts_index, ].CLOSE
return self.holding * cost

def getFitness(self):
fitness = ((self.balance + self.getHoldingsValue()) - self.starting_balance) / self.starting_balance
if self.__counter > self.__penalty_time and self.__buy_counter < 1:
#print(f"Genome has not bought anything in {self.__penalty_time} steps. Applying penalty.")
fitness -= (self.__PENALTY * self.__penalty_mult)
self.__penalty_mult += 1
return fitness


def getTrade(action):
# print(action)
if action[0] < 0.33:
return ("SELL", action[1])
elif action[0] >= 0.33 and action[0] < 0.66:
Expand Down
Loading

0 comments on commit 07d692c

Please sign in to comment.