text:
<repo_name>benkissi/goldenrod<file_sep>/lib/collections/transactions.js
Transactions = new Mongo.Collection('transactions');

Transactions.allow({
	update: function(userId, account) { return ownsDocument(userId, account); },
	remove: function(userId, account) { return ownsDocument(userId, account); },
});

validateTransaction = function (transaction) {
	var errors = {};
	if (!transaction.transactionDate)
		errors.transactionDate = "Please fill in the transaction date";
	if (!transaction.transactionAmount)
		errors.transactionAmount = "Please fill in the transaction amount";
	return errors;
}

Meteor.methods({
	transactionInsert: function(transactionAttributes){
		check(Meteor.userId(), String);
		check(transactionAttributes, {
			transactionDate : String,
			accountNumber: String,
			accountName: String,
			transactionType: String,
			transactionAmount : Number,
			balance : Number
		});

		var user = Meteor.user();
		var transaction = _.extend(transactionAttributes, {
			userId: user._id,
			author: user.username,
			submitted: new Date()
		});
		var transactionId = Transactions.insert(transaction);
		return {
			_id: transactionId
		};
	}
});
<file_sep>/client/template/accounts/accountEdit.js
Template.accountEdit.created = function() {
	Session.set('accountSubmitErrors', {});
}

Template.accountEdit.helpers({
	errorMessage: function(field) {
		return Session.get('accountSubmitErrors')[field];
	},
	errorClass: function (field) {
		return !!Session.get('accountSubmitErrors')[field] ? 'has-error' : '';
	}
});

Template.accountEdit.rendered = function(){
	var id = Router.current().params['_id'];
	var picID = Acounts.findOne(id).pictureID;

	$('.datepicker').datepicker({
		format: 'dd/mm/yyyy',
		startDate: '-3d'
	})
	return picID;
}

Template.accountEdit.events({
	'change .fileInput': function(event, template) {
		Session.set('fileChange', true);
		FS.Utility.eachFile(event, function(file) {
			Pictures.insert(file, function (err, fileObj) {
				if (err){
					console.log(err);
				} else {
					var userId = Meteor.userId();
					image ={
						imageURL:"/cfs/files/pictures/" + fileObj._id,
						imageID: fileObj._id
					}
					return image;
				}
			});
		});
	},
	'submit form': function(e) {
		e.preventDefault();

		var currentAccountId = this._id;
		var id = Router.current().params['_id'];
		var picURL = Acounts.findOne(id).picture;

		var imageID = Acounts.findOne(id).pictureID;

		imagePath = ""
		if(Session.get("fileChange")) {
			imagePath = image.imageURL;

		} else {
			imagePath = picURL;
		}

		var accountProperties = {
			accountNumber: $(e.target).find('[name=accountNumber]').val(),
			accountName: $(e.target).find('[name=accountName]').val(),
			accountTotal: $(e.target).find('[name=amount]').val(),
			dateCreated: $(e.target).find('[name=dateCreated]').val(),
			idType: $(e.target).find('[name=idType]').val(),
			idNumber: $(e.target).find('[name=idNumber]').val(),
			picture:  imagePath,
			pictureID: imageID
		}

		var errors = validateAccount(accountProperties);
		if (errors.accountName||errors.accountNumber||errors.accountTotal||errors.dateCreated
			||errors.idType||errors.idNumber)
			return Session.set('accountSubmitErrors', errors);

		Acounts.update(currentAccountId, {$set: accountProperties}, function(error) {
			if (error) {
			// display the error to the user
				return Error.throw(error.reason);
		} else {
			Router.go('accountPage', {_id: currentAccountId});
		}
	});
	},
	'click .delete': function(e) {
		e.preventDefault();
		if (confirm("Delete this account?")) {
			var currentAccountId = this._id;

			var id = Router.current().params['_id'];
			var imageID = Acounts.findOne(id).pictureID;
			if (imageID){
				Pictures.remove(imageID);
				Acounts.remove(currentAccountId);
			}else{
				Acounts.remove(currentAccountId);
			}

			Router.go('accounts');
		}
	}
});<file_sep>/server/publications.js
Meteor.publish('acounts', function(){
	return Acounts.find();
})

Meteor.publish('transactions', function(){
	return Transactions.find();
})

Meteor.publish("pictures", function(){
  return Pictures.find();
});<file_sep>/client/main.js
Meteor.subscribe('acounts');
Meteor.subscribe('transactions');
Meteor.subscribe('pictures');<file_sep>/client/template/accounts/accounts.js
Template.accounts.helpers({
	acounts : function(){
		return Acounts.find({}, {sort: {submitted: -1}});
	}
});

<file_sep>/client/template/accounts/accountPage.js
Template.accountPage.rendered = function(){
	$('.d-c').hide();
}

Template.accountPage.created = function() {
	Session.set('transactionSubmitErrors', {});
}
Template.newAccount.rendered = function(){
	$('.datepicker').datepicker({
		format: 'dd/mm/yyyy',
		startDate: '-3d'
	})
}

Template.accountPage.helpers({
	errorMessage: function(field) {
		return Session.get('transactionSubmitErrors')[field];
	},
	errorClass: function (field) {
		return !!Session.get('transactionSubmitErrors')[field] ? 'has-error' : '';
	}

});

Template.accountPage.events({
	'submit form': function(e){
		e.preventDefault();
		var currentAccountId = this._id;
		var transactionAmount = parseFloat($(e.target).find('[name = transactionAmount]').val());
		var credit = {
			accountTotal : (parseFloat(this.accountTotal) + transactionAmount)
		}

		var accountNumber = this.accountNumber;
		var accountName = this.accountName;
		var transactionDate = $('#transDate').val();

		var transaction = {
			transactionDate : transactionDate,
			accountNumber: accountNumber,
			accountName: accountName,
			transactionType: "credit",
			transactionAmount : transactionAmount,
			balance : credit.accountTotal
		}

		var errors = validateTransaction(transaction);
		if (errors.transactionAmount||errors.transactionDate)
			return Session.set('transactionSubmitErrors', errors);

		if (confirm("Credit this account?")) {
			Acounts.update(currentAccountId, {$set: credit}, function(error) {
				if (error) {
					// display the error to the user
					alert(error.reason);
				} else {
					Router.go('accountPage', {_id: currentAccountId});
					error = {};
					return Session.set('transactionSubmitErrors', errors);
				}
			});
		}

		Meteor.call('transactionInsert', transaction, function(error, result) {
			// display the error to the user and abort
			if (error)
				return alert(error.reason);

			$('.transactionAmount').val('');
			$('#transDate').val('')
		});
	},

	'click .debit'	: function(e){
		e.preventDefault();
		var currentAccountId = this._id;
		var transactionAmount = parseFloat($('.transactionAmount').val());
		var debit = {
			accountTotal : (parseFloat(this.accountTotal) - transactionAmount)
		}

		var accountNumber = this.accountNumber;
		var accountName = this.accountName;
		var transactionDate = $('#transDate').val();

		var transaction = {
			transactionDate : transactionDate,
			accountNumber: accountNumber,
			accountName: accountName,
			transactionType: "debit",
			transactionAmount : transactionAmount,
			balance : debit.accountTotal
		}
		var errors = validateTransaction(transaction);
		if (errors.transactionAmount||errors.transactionDate)
			return Session.set('transactionSubmitErrors', errors);

		if(confirm('Do you want to debit this account')){
			Acounts.update(currentAccountId, {$set: debit}, function(error) {
				if (error) {
				// display the error to the user
				alert(error.reason);
			} else {
				Router.go('accountPage', {_id: currentAccountId});
				error = {};
				return Session.set('transactionSubmitErrors', errors);

			}
		});
		}

		Meteor.call('transactionInsert', transaction, function(error, result) {
			// display the error to the user and abort
			if (error)
				return alert(error.reason);

			$('.transactionAmount').val('');
			$('#transDate').val('')
		});
	},

	'click .debit-credit': function(e){
		e.preventDefault();
		$('.d-c').slideToggle("fast","swing");
	}
})
<file_sep>/lib/collections/accounts.js
Acounts = new Mongo.Collection('acounts');

Acounts.allow({
	update: function(userId, account) { return ownsDocument(userId, account); },
	remove: function(userId, account) { return ownsDocument(userId, account); }
});

Acounts.deny({
	update: function(userId, account, fieldNames) {
// may only edit the following two fields:
return (_.without(fieldNames, 'accountNumber', 'accountName', 'accountTotal', 'dateCreated',
	'idType','idNumber','picture').length > 0);
}
});

validateAccount = function (account) {
	var errors = {};
	if (!account.accountName)
		errors.accountName = "Please fill in the account name";
	if (!account.accountNumber)
		errors.accountNumber = "Please fill in the account number";
	if (!account.accountTotal)
		errors.accountTotal = "Please fill in the amount";
	if (!account.dateCreated)
		errors.dateCreated = "Please fill in the date";
	if (!account.idType)
		errors.idType = "Please fill in the ID type";
	if (!account.idNumber)
		errors.idNumber = "Please fill in the ID number";
	if (!account.picture)
		errors.idNumber = "Please fill in the ID number";
	return errors;
}

Acounts.deny({
	update: function(userId, post, fieldNames, modifier) {
		var errors = validatePost(modifier.$set);
		return errors.accountName||errors.accountNumber||errors.accountTotal||errors.dateCreated
			||errors.idType||errors.idNumber;
	}
});

Meteor.methods({
	accountsInsert: function(accountAttributes) {
		check(Meteor.userId(), String);
		check(accountAttributes, {
			accountNumber: String,
			accountName: String,
			accountTotal: Number,
			dateCreated: String,
			idType : String,
			idNumber :String,
			picture : String,
			pictureID: String
		});

		var accountWithSameNumber = Acounts.findOne({accountNumber: accountAttributes.accountNumber});
		if (accountWithSameNumber) {
			return {
				accountExists: true,
				_id: accountWithSameNumber._id
			}
		}
		var user = Meteor.user();
		var account = _.extend(accountAttributes, {
			userId: user._id,
			author: user.username,
			submitted: new Date()
		});
		var accountId = Acounts.insert(account);
		return {
			_id: accountId
		};
	}
});

repo_name: benkissi/goldenrod


text:
<repo_name>donatodaniel/donatodaniel.github.io<file_sep>/scripts/match.js
/*
  Important = Player (who will play the game) | Opponnet  = the Machine(the logics created under the hood)
*/

$(document).ready(function () {

  let audio = new Audio("audio/bgaudio.mp3");
  audio.oncanplaythrough = function () {
    audio.play();
    audio.loop = true;
  }

  // Add a class 'active' to the first card of the player so that he can use right and left arrow to select his cards
  $("#card1").addClass("active");
  $("#fist1").addClass("activeActive");

  //Sets Opponnets and Players Life Points and Strength points on Dashboard
  newGame.totals();
  $("#oppDash").html("Machine
 " + newGame.totals()[0] + "  " + newGame.totals()[1] + "");
  $("#playerDash").html("You
 " + newGame.totals()[2] + "  " + newGame.totals()[3] + "
XP "+ newGame.playerExperience+"");

});

/* There are being created variables to control which div the user is currently on whenever his moving to the right or left
  amountCards = current card
  amountPower = is how many strength was selected to be played
  fightTime  = is used to count the second time button 'Enter' is pressed, so that it can set the battleField to appear on the screen
*/
let amountCards = 0;
let amountPower = 1;
let fightTime = 0;
document.onkeydown = function (e) {

  /*Here we have the control for the keys pressed by the user and change classes so that the elements appear of be heighlighted in the DOM
  If its in Pickup card mode users moves right or left
  If its in Select the Attach mode it increases the amountPower
  If pressed Esc user can decide not to continue with that card
  */

  switch (e.keyCode) {
    //Left Button
    case 37:
      if (newGame.strengthSelection == 0) {
        if (amountCards >= 1) {
          $("div[class~='active'").prev().addClass("active");
          $("div[class~='active'").next().removeClass("active");
          let cardChange = new Audio('audio/cardChange.mp3');
          cardChange.play();
          amountCards--;
        }
      }
      if (newGame.strengthSelection == 1) {
        if (amountPower >= 2) {
          $("div[class~='fistActive'").last().removeClass("fistActive");
          $("#fist1").addClass("activeActive");
          let cardChange = new Audio('audio/cardChange.mp3');
          cardChange.play();
          amountPower--;
        }
        newGame.attackSetter(amountCards, amountPower);
      }
      break;
    // Right Button
    case 39:
      if (newGame.strengthSelection == 0) {
        if (amountCards <= 2) {
          $("div[class~='active'").next().addClass("active");
          $("div[class~='active'").prev().removeClass("active");
          let cardChange = new Audio('audio/cardChange.mp3');
          cardChange.play();
          $("#card1").removeClass("active");
          amountCards++;
        }
      }
      if (newGame.strengthSelection == 1) {
        if (amountPower <= (newGame.strengthPlayer - 1)) {
          $("div[class~='fistActive'").next().addClass("fistActive");
          amountPower++;
        }
        let fireIncrease = new Audio('audio/fireIncrease.mp3');
        fireIncrease.play();
        newGame.attackSetter(amountCards, amountPower);
      }
      break;
    case 13:
      if (newGame.strengthSelection == 0) {
        newGame.strengthSelection++;
        $("#attack").css("visibility", "visible");
      } else {
        newGame.strengthSelection--;
        $("#attack").css("visibility", "hidden")
      }
      if (fightTime >= 1) {
        newGame.playerChoosesFirst(amountCards, amountPower);
        newGame.showAnimation();
        let prepare = new Audio('audio/prepareYourself.mp3');
        prepare.play();
        amountPower = 1;
        $("div[class~='fistActive'").first().nextAll().removeClass("fistActive");
        fightTime = 0;
      } else {
        fightTime++;
        newGame.attackSetter(amountCards, amountPower);
      }
      break;
    case 27:
      if (newGame.strengthSelection == 1) {
        newGame.strengthSelection--;
        $("#attack").css("visibility", "hidden")
      }
      if (fightTime > 0) {
        fightTime--;
        $("div[class~='fistActive'").first().nextAll().removeClass("fistActive");
        amountPower = 1;
      }

      break;

  }
}
// Create a match constructor to start the Game with all variables needed for the game
let Match = function (cards) {
  this.cards = cards;
  this.level = 0;
  this.round = 1;
  this.round2 = 0;
  this.round3 = 0;
  this.round4 = 0;
  this.whoStarts = 1;
  this.strengthSelection = 0;

  this.lifePointsPlayer = 12;
  this.strengthPlayer = 12;
  this.attack = 0;
  this.pickedCardsPlayer = [];
  this.usedCardsPlayer = [];
  this.playerExperience = 0;

  this.lifePointsOpp = 12;
  this.strengthOpp = 12;
  this.pickedCardsOpp = [];
  this.usedCardsOpp = [];
  this.oppStrategy = [];
  this.log = [];

};

// Suffle the array of cards
Match.prototype.shuflleCards = function () {

  for (let i = this.cards.length - 1; i >= 0; i--) {
    let randomNum = Math.floor(Math.random() * (i + 1));
    let value = this.cards[randomNum];
    this.cards[randomNum] = this.cards[i];
    this.cards[i] = value;
  }

  // Decide who starts the first round if it's 1 so the Plater should start otherwhise the opponent starts
  this.whoStarts = Math.random() > 0.5 ? 1 : 0;

};

// Choose  4 random cards for both the player and the Opponent and set them as background in the DOM Divs
Match.prototype.pickCards = function () {
  for (j = 0; j <= 3; j++) {
    this.pickedCardsPlayer.push(this.cards[j]);
    $("#card" + (j + 1)).css("background-image", "url('images/" + this.pickedCardsPlayer[j].img + "')");
    $("#card" + (j + 1)).css("background-repeat", "no-repeat");
    $("#card" + (j + 1)).css("background-size", "cover");
    $("#card" + (j + 1)).css("background-size", "105% 105%");
    $("#card" + (j + 1)).css("background-position", "center");
    $("#card" + (j + 1)).attr("title", this.pickedCardsPlayer[j].description);
    this.cards[j].status = true;
  }

  for (k = 4; k <= 7; k++) {
    this.pickedCardsOpp.push(this.cards[k]);
    $("#opp" + k).css("background-image", "url('images/" + this.pickedCardsOpp[(k - 4)].img + "')");
    $("#opp" + k).css("background-repeat", "no-repeat");
    $("#opp" + k).css("background-size", "cover");
    $("#opp" + k).css("background-size", "105% 105%");
    $("#opp" + k).css("background-position", "center");
    $("#opp" + k).attr("title", this.pickedCardsOpp[(k - 4)].description);
    this.cards[k].status = true;
  }
};
// For each round the script chooses one out of the 4 card the Opponent has to battle, and also chooses how many strength capsules are going to be used against player
Match.prototype.opponentStrategy = function () {
  let oppAttack = 0;
  let chooseCard = 0;
  if (this.round == 1) {
    chooseCard = Math.floor(Math.random() * this.pickedCardsOpp.length);
    this.pickedCardsOpp[chooseCard].status = false;
    this.usedCardsOpp.push(this.pickedCardsOpp[chooseCard]);
    oppAttack = this.pickedCardsOpp[chooseCard].power * (this.strengthOpp <= 1 ? 1 : Math.floor(Math.random() * (this.strengthOpp)));

  } else if (this.round == 2) {
    this.round2 = this.pickedCardsOpp.filter(function (e) {
      return e.status == true;
    });
    chooseCard = Math.floor(Math.random() * this.round2.length);
    this.round2[chooseCard].status = false;
    this.usedCardsOpp.push(this.round2[chooseCard]);
    oppAttack = this.round2[chooseCard].power * (this.strengthOpp <= 1 ? 1 : Math.floor(Math.random() * (this.strengthOpp)));

  } else if (this.round == 3) {

    this.round3 = this.round2.filter(function (f) {
      return f.status == true;
    });
    chooseCard = Math.floor(Math.random() * this.round3.length);
    this.round3[chooseCard].status = false;
    this.usedCardsOpp.push(this.round3[chooseCard]);
    oppAttack = this.round3[chooseCard].power * (this.strengthOpp <= 1 ? 1 : Math.floor(Math.random() * (this.strengthOpp)));

  } else {
    this.round4 = this.round3.filter(function (g) {
      return g.status == true;
    });
    chooseCard = this.round4.length;
    this.round4[0].status = false;
    this.usedCardsOpp.push(this.round4[0]);
    oppAttack = this.round4[0].power * (this.strengthOpp <= 1 ? 1 : Math.floor(Math.random() * this.strengthOpp));
  }

  return [this.usedCardsOpp[this.usedCardsOpp.length - 1], oppAttack == 0 ? this.usedCardsOpp[this.usedCardsOpp.length - 1].power : oppAttack];
}

// Receive a card and see what is the card Power and multiplies the Power of the card by the strength chosen
// The strategy of the Opponent and the player strategy Card and attack for each one receives the index of the card the player wants to use
Match.prototype.playerChoosesFirst = function (index, strength) {

  this.card = this.pickedCardsPlayer[index];
  this.attack = this.card.power * strength;
  this.oppStrategy = this.opponentStrategy();
  let ind = this.pickedCardsOpp.indexOf(this.oppStrategy[0]);

  setTimeout(function () {
    let newBattle = new Audio('audio/fight.mp3');
    newBattle.play();

    let fightSound = new Audio('audio/FightSound.mp3');
    fightSound.play();
  }, 1750)

  //Even though the player or opponnent run out of Strength points they always be granted with at least 1 Strength Point
  if (this.strengthPlayer < 1) {
    this.strengthPlayer = 1;
  } else {
    this.strengthPlayer -= strength;
  }

  let oppAttack = this.oppStrategy[1] == 0 ? this.oppStrategy[0].power : this.oppStrategy[1];
  if (this.strengthOpp < 1) {
    this.strengthOpp = 1;
  } else {
    this.strengthOpp -= (this.oppStrategy[1] / this.oppStrategy[0].power);
  }

  //Show the battlefield and the current cards to be dueling in the field
  $("#battlefield").css("background-image", "url(images/fightingArena" + this.round + ".jpg)");
  $("#oppTitle").html("Machine
" + oppAttack + "
 " + this.oppStrategy[0].power + " X " + (oppAttack / this.oppStrategy[0].power));
  $("#oppAttack").css("background-image", "url('images/" + this.pickedCardsOpp[ind].img + "')");
  $("#playerAttack").css("background-image", "url('images/" + this.pickedCardsPlayer[index].img + "')");
  $("#oppAttack").css("background-repeat", "no-repeat");
  $("#oppAttack").css("background-size", "cover");
  $("#oppAttack").css("background-size", "105% 105%");
  $("#oppAttack").css("background-position", "center");
  $("#playerTitle").html("You
" + this.attack + "
 " + this.card.power + " X " + (this.attack / this.card.power));
  $("#playerAttack").css("background-repeat", "no-repeat");
  $("#playerAttack").css("background-size", "cover");
  $("#playerAttack").css("background-size", "105% 105%");
  $("#playerAttack").css("background-position", "center");

  //At this moment we compare who wins the current round based on who has the bigger attack and zeros the Player attack for next round
  if (this.attack > oppAttack) {
    this.lifePointsOpp -= this.card.damage;
    this.playerExperience += (this.attack - oppAttack);
    $("#oppAttack").addClass("defeated");
    $('#opp' + (ind + 4)).html("");
    $('#opp' + (ind + 4)).addClass("defeated");
    $('#card' + (index + 1)).html("");

    setTimeout(function () {
      newResult = new Audio('audio/youwin.mp3');
      newResult.play();
    }, 6500);
    this.attack = 0;

  } else if (this.attack == oppAttack) {

    this.attack = 0;
  } else {
    this.lifePointsPlayer -= this.oppStrategy[0].damage;
    this.attack = 0;
    this.oppStrategy[0].status = false;
    $('#card' + (index + 1)).html("");
    $('#card' + (index + 1)).addClass("defeated");
    $('#opp' + (ind + 4)).html("");
    $("#playerAttack").addClass("defeated");
    setTimeout(function () {
      newResult = new Audio('audio/youlose.mp3');
      newResult.play();
    }, 6500);

  }

  //Show the effect that seems lightbeams are being triggered between dueling cards
  setTimeout(function () {
    $('#battlefield').css('visibility', 'visible');
    $('canvas').css('visibility', 'visible');
    let audio = new Audio('audio/attack.mp3');
    audio.play();

    setTimeout(function () {
      $('#battlefield').css('visibility', 'hidden');
      $('canvas').css('visibility', 'hidden');
      $("#playerAttack").removeClass("defeated");
      $("#oppAttack").removeClass("defeated");
    }, 9000);
  }, 1200);

  //Update the round Dashboards
  this.updateRound();
  this.updateDash();

};

Match.prototype.updateRound = function () {
  if (this.round > 3) {
    this.endOfMatch();
    this.round = 1;
  } else {
    this.round++;

  }
}

Match.prototype.updateLoserCard = function (index) {

}

Match.prototype.updateDash = function () {
  $("#oppDash").html("Machine
 " + this.lifePointsOpp + " " + this.strengthOpp + "
");
  $("#playerDash").html("You
 " + this.lifePointsPlayer + " " + this.strengthPlayer + "
XP "+ this.playerExperience+"");
}

Match.prototype.totals = function () {
  return [this.lifePointsOpp, this.strengthOpp, this.lifePointsPlayer, this.strengthPlayer];
};

Match.prototype.showAnimation = function () {

};

Match.prototype.endOfMatch = function () {
  let newResult;
  let winner = '';
  if (this.lifePointsPlayer > this.lifePointsOpp) {
    winner = 'You win! You\'ve increased your XP points to ' + this.playerExperience + '!';
  } else if (this.lifePointsPlayer < this.lifePointsOpp) {
    winner = 'Game Over! The Machine is the winner';
    this.playerExperience = 0;
  } else {
    winner = 'It\'s a draw';

  }
  setTimeout(function () {

    newResult = new Audio('audio/matchEnd.mp3');
    newResult.play();
    alert(winner);
  }, 5500);
  this.pickedCardsOpp = [];
  this.pickedCardsPlayer = [];
  this.lifePointsOpp = 12;
  this.lifePointsPlayer = 12;
  this.strengthOpp = 12;
  this.strengthPlayer = 12;
  this.round2 = [];
  this.round3 = [];
  this.round4 = [];
  this.shuflleCards();
  this.pickCards();
  $('.cells').removeClass('defeated');
  $('.cells').html('');

}

Match.prototype.attackSetter = function (index, amountPower) {

  $("#selectedCard").css("background-image", "url('images/" + this.pickedCardsPlayer[index].img + "')");
  $("#selectedCard").css("background-repeat", "no-repeat");
  $("#selectedCard").css("background-size", "cover");
  $("#selectedCard").css("background-size", "105% 105%");
  $("#selectedCard").css("background-position", "center");
  $("#selectedCard").css("filter", "none");
  $("#selectedDescription").html("
<stron>Attack: " + (amountPower * this.pickedCardsPlayer[index].power) + "

<stron>Name:</stron> " + this.pickedCardsPlayer[index].name + "
<stron>Element:</stron> " + this.pickedCardsPlayer[index].element + "
<stron>Description: " + this.pickedCardsPlayer[index].description + "
");

}

Match.prototype.createMatch = function () {
  for (let g = 1; g <= 4; g++) {
    // round 1
    switch (this.round) {
      case 1:
        if (this.whoStarts == 1) {
          //Player start to select cards
        } else {
          //Show opponents cards
        }
        break;
      case 2:
        if (this.whoStarts == 1) {
          //Show opponents cards
        } else {
          //Player start to select cards
        }
        break;
      case 3:
        if (this.whoStarts == 1) {
          //Player start to select cards
        } else {
          //Show opponents cards
        }
        break;
      case 4:
        if (this.whoStarts == 1) {
          //Show opponents cards
        } else {
          //Player start to select cards
        }
        break;
    }

  }

}

let newGame = new Match(deck);

newGame.shuflleCards();
newGame.pickCards();
newGame.createMatch();

<file_sep>/scripts/beam.js
/*

Beams.
Author: <NAME> 2016

*/

// Main elements
const canvas = document.createElement('canvas'),
      ctx = canvas.getContext('2d');

// Vars
let beams_count = 0,
    beams = [];

// Size and set the stage
canvas.height = 400;
canvas.width = 100;

// Build the stage
document.body.appendChild(canvas);

// Platform
class Platform {

  /*
	------------------------------------------
	| constructor:void (-)
	|
	| Construct.
	------------------------------------------ */
  constructor(){
    this.color = '#ffffdc';
    this.width = 200;
    this.height = 200;
    this.segments = (this.width / 10);

    this.position = {
      x: (canvas.width / 2) - (325 / 2),
      y: (canvas.height / 2) - (325 / 2),
      rot: 0
    };

    // Points for the grid
    this.points = [];
    for( let i = 0; i < 5; i++ ){
      let p = this.segments * i;
      this.points.push(p);
    }

  }

  /*
	------------------------------------------
	| update:void (-)
	|
	| update.
	------------------------------------------ */
  update() {
    this.position.rot += .01;
  }

  /*
	------------------------------------------
	| draw:void (-)
	|
	| Draw.
	------------------------------------------ */
  draw(){
    let offset_x = this.position.x + (this.width /2),
        offset_y = this.position.y + (this.height /2)
    ctx.save();

    // Translate & rotate platform
    ctx.translate(offset_x,offset_y);
    ctx.strokeStyle = this.color;
    ctx.lineWidth = 2;

    // Transform the canvas to give perspective
    ctx.setTransform(1, -.13, 1.7, .1, this.position.x + this.width, this.position.y + 75);

    // Rotate
    ctx.rotate(this.position.rot);

    // Draw X
    for( let i = 0; i < this.points.length; i++ ){
      let x = this.points[i] - (this.width / 2),
          half = this.height - (this.height / 2);

      ctx.beginPath();
      ctx.moveTo(x, -(this.width / 2));
      ctx.lineTo(x, half);
      ctx.closePath();

    //   ctx.shadowColor = this.color;
    //   ctx.shadowBlur = 10;
    //   ctx.shadowOffsetX = 0;
    //   ctx.shadowOffsetY = 0;

      ctx.stroke();
    }

    // drawy Y
    for( let i = 0; i < this.points.length; i++ ){
      let y = this.points[i] - (this.width / 2),
          half = this.width - (this.width / 2);

      ctx.beginPath();
      ctx.moveTo(-half, y);
      ctx.lineTo(half, y);
      ctx.closePath();

    //   ctx.shadowColor = this.color;
    //   ctx.shadowBlur = 10;
    //   ctx.shadowOffsetX = 0;
    //   ctx.shadowOffsetY = 0;

      ctx.stroke();
    }

    // Draw the rect
    ctx.rect(-(this.width / 2), -(this.height / 2), this.width, this.height);

    // ctx.shadowColor = this.color;
    // ctx.shadowBlur = 10;
    // ctx.shadowOffsetX = 0;
    // ctx.shadowOffsetY = 0;

    // Stroke & reset
    ctx.stroke();
    ctx.restore();
  }
}

// Beams
class Beam {

  /*
	------------------------------------------
	| constructor:void (-)
	|
	| Construct.
	------------------------------------------ */
  constructor( init_obj ){

    // Config
    this.color = '#ffffdc';
    this.range_x = [ (canvas.width / 2) - 150, (canvas.width / 2) + 150];
    this.range_y = [ (canvas.height / 2), -100];

    // Generate random width
    this.width = () => {
      return (Math.random() * (2 - 1)) + 1;
    }

    // Generate a random target height
    this.height = (Math.random() * (25 - 55)) + 70;

    // Start X & Y
    this.start_x = (Math.random() * (this.range_x[0] - this.range_x[1])) + this.range_x[1];
    this.start_y = (Math.random() * ((canvas.height / 2) - 5) - (canvas.height/2)) + (canvas.height / 1);

    // Growth & Speed
    this.growth = (Math.random() * (1 - 3)) + 1;
    this.speed = (Math.random() * (1 - 2)) + 2;

    // Opacity
    this.opacity = 1;

    // Current settings
    this.current_x = this.start_x;
    this.current_y = this.start_y;
    this.current_h = Math.random() * 50;
    this.current_o = this.opacity;
  }

  /*
	------------------------------------------
	| update:void (-)
	|
	| Update.
	------------------------------------------ */
  update(){

    this.current_h > this.height ? this.current_h = this.height : this.current_h += this.growth;
    this.current_y -= this.speed;

  }

  /*
	------------------------------------------
	| draw:void (-)
	|
	| Draw.
	------------------------------------------ */
  draw(){
    ctx.save();
    ctx.beginPath();
    ctx.lineWidth = this.width();
    ctx.moveTo(this.current_x, this.current_y);
    ctx.lineTo(this.current_x, this.current_y + this.current_h);
    ctx.strokeStyle = this.color;

    // ctx.shadowColor = this.color;
    // ctx.shadowBlur = 10;
    // ctx.shadowOffsetX = 0;
    // ctx.shadowOffsetY = 0;

    ctx.stroke();
    ctx.fill();
    ctx.restore();
  }
}

// Sphere
class Sphere {

  /*
	------------------------------------------
	| constructor:void (-)
	|
	| Construct.
	------------------------------------------ */
  constructor( init_obj ){

    this.colors = init_obj.colors;
    this.radius = init_obj.radius;
    this.position = {
      x: init_obj.x,
      y: init_obj.y
    }

  }

  /*
	------------------------------------------
	| update:void (-)
	|
	| Update.
	------------------------------------------ */
  update(){

  }

  /*
	------------------------------------------
	| draw:void (-)
	|
	| Draw.
	------------------------------------------ */
  draw(){
    ctx.save();
    ctx.beginPath();
    ctx.arc( this.position.x, this.position.y, this.radius, 0, 2 * Math.PI, false);

    let grd = ctx.createRadialGradient(
      this.position.x,
      this.position.y - (this.radius + (this.radius * 2)),
      2,
      this.position.x,
      this.position.y - (this.radius + (this.radius / 2)),
      this.radius * 2
    );

    // grd.addColorStop(0, this.colors[1]);
    // grd.addColorStop(1, this.colors[0]);

    // Fill with gradient
    ctx.fillStyle = grd;
    ctx.fill();
    ctx.restore();
  }
}

// Background
class Background {

  /*
	------------------------------------------
	| constructor:void (-)
	|
	| Construct.
	------------------------------------------ */
  constructor(){

    // Colors
    this.colors = {
      start: '#000',
      stop: '#000'
    }

  }

	/*
	------------------------------------------
	| draw:void (-)
	|
	| Draw.
	------------------------------------------ */
  draw(){
    let grd = ctx.createRadialGradient(
      canvas.width / 2,
      300,
      0,
      canvas.width / 2,
      -400,
      canvas.width * 2
    );
    // grd.addColorStop(0, this.colors.start);
    // grd.addColorStop(1, this.colors.stop);

    // Fill with gradient
    ctx.fillStyle = grd;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
  }
}

// Scene
class Scene {

  /*
	------------------------------------------
	| constructor:void (-)
	|
	| Construct.
	------------------------------------------ */
  constructor(){

    // Background
    this.bg = new Background();

    // Sphere
    this.sphere = new Sphere({
      colors: ['#3c3949', '#2e2e2e'],
      radius: 88,
      x: canvas.width / 2,
      y: canvas.height / 2 + 60
    });

    // Small Sphere
    this.small_sphere = new Sphere({
      colors: ['#3c3949', '#b4645d'],
      radius: 10,
      x: canvas.width / 2 + 110,
      y: canvas.height / 2 - 40
    });

    // Beams - put in a delay so randomness happens
    let beam_maker = setInterval( () => {
      if( beams.length < 25 ){
        beams.push(new Beam());
        beams_count++;
      } else {
        clearInterval(beam_maker);
      }
    },250);

    // Platform
    this.g_platform = new Platform();

    // kick things off
    this.init();
  }

  /*
	------------------------------------------
	| init:void (-)
	|
	| Init.
	------------------------------------------ */
  init(){
    this.update();
  }

  /*
	------------------------------------------
	| update:void (-)
	|
  | If we need to update anything...
  |
	| Update.
	------------------------------------------ */
  update(){

    // Update Spheres
    this.sphere.update();
    this.small_sphere.update();

    // Create new beams
    for(let i = beams.length; i < 200; i++ ){
      beams.push(new Beam());
    }

    // Update Beams
    for( let i = 0; i < beams.length; i++ ){
      if( beams[i].current_y < beams[i].range_y[1] ){
        beams.splice(i,1);
      } else {
        beams[i].update();
      }
    }

    // Update Platform
    this.g_platform.update();

    // Draw
    this.draw();
  }

  /*
	------------------------------------------
	| draw:void (-)
	|
	| Draw.
	------------------------------------------ */
  draw(){
    // Clear the stage
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Draw the bg
    this.bg.draw();

    // Spheres
    //this.sphere.draw();
    //this.small_sphere.draw();

    // Platform
    //this.g_platform.draw();

    // Beams
    for( let i = 0; i < beams.length; i++ ){
      beams[i].draw();
    }

    // Animate!
    requestAnimationFrame( () => this.update() );
  }
}

// Setup
new Scene();<file_sep>/scripts/cardsOfWar.js
// Create all cards of the game
let deck = [
  { name: 'CHAMAELEON', description: 'THE CHAMELEON', power: 3, damage: 2, element: 'EARTH', img: 'CHAMAELEON.PNG', status: true },
  { name: 'PARDALIS', description: 'THE GIRAFFE', power: 4, damage: 2, element: 'EARTH', img: 'PARDALIS.PNG', status: true },
  { name: 'CORVUS', description: 'THE RAVEN', power: 8, damage: 5, element: 'WIND', img: 'CORVUS.PNG', status: true },
  { name: 'VULPECULA', description: 'THE LITTLE FOX', power: 2, damage: 4, element: 'EARTH', img: 'VULPECULA.PNG', status: true },
  { name: 'LACERTA', description: 'THE LIZARD', power: 5, damage: 2, element: 'EARTH', img: 'LACERTA.PNG', status: true },
  { name: 'CETUS', description: 'THE WHALE', power: 5, damage: 2, element: 'WATER', img: 'CETUS.PNG', status: true },
  { name: 'INDUS', description: 'THE INDIAN', power: 5, damage: 3, element: 'EARTH', img: 'INDUS.PNG', status: true },
  { name: 'VOLANS', description: 'THE FLYING FISH', power: 7, damage: 2, element: 'WATER', img: 'VOLANS.PNG', status: true },
  { name: 'PAVO', description: 'THE PEACOCK', power: 7, damage: 2, element: 'EARTH', img: 'PAVO.PNG', status: true },
  { name: 'OCTANS', description: 'THE OCTANT', power: 5, damage: 4, element: 'WATER', img: 'OCTANS.PNG', status: true },
  { name: 'OPHIUCHUS', description: 'THE SERPENT HOLDER', power: 5, damage: 4, element: 'EARTH', img: 'OPHIUCHUS.PNG', status: true },
  { name: 'DORADO', description: 'THE DOLPHINFISH', power: 5, damage: 4, element: 'WATER', img: 'DORADO.PNG', status: true },
  { name: 'MUSCA', description: 'THE FLY', power: 4, damage: 5, element: 'WIND', img: 'MUSCA.PNG', status: true },
  { name: '<NAME>', description: 'THE HUNTING DOGS', power: 8, damage: 2, element: 'EARTH', img: 'CANES VENATICI.PNG', status: true },
  { name: 'APUS', description: 'THE BIRD OF PARADISE', power: 7, damage: 3, element: 'WIND', img: 'APUS.PNG', status: true },
  { name: 'CYGNUS', description: 'THE SWAN', power: 7, damage: 3, element: 'WATER', img: 'CYGNUS.PNG', status: true },
  { name: 'EQUULEUS', description: 'THE LITTLE HORSE', power: 7, damage: 4, element: 'EARTH', img: 'EQUULEUS.PNG', status: true },
  { name: 'SAGITTARIUS', description: 'THE ARCHER', power: 6, damage: 3, element: 'EARTH', img: 'SAGITTARIUS.PNG', status: true },
  { name: 'ORION', description: 'THE HUNTER', power: 6, damage: 5, element: 'EARTH', img: 'ORION.PNG', status: true },
  { name: 'DRACO', description: 'THE DRAGON', power: 6, damage: 5, element: 'FIRE', img: 'DRACO.PNG', status: true },
  { name: 'CEPHEUS', description: 'THE KING', power: 10, damage: 2, element: 'EARTH', img: 'CEPHEUS.PNG', status: true },
  { name: 'MONOCEROS', description: 'THE UNICORN', power: 9, damage: 3, element: 'EARTH', img: 'MONOCEROS.PNG', status: true },
  { name: 'PEGASUS', description: 'THE WINGED HORSE', power: 9, damage: 3, element: 'WIND', img: 'PEGASUS.PNG', status: true },
  { name: 'LYRA', description: 'THE HARP', power: 9, damage: 3, element: 'EARTH', img: 'LYRA.PNG', status: true },
  { name: 'PERSEUS', description: 'THE GREEK HERO', power: 8, damage: 4, element: 'WIND', img: 'PERSEUS.PNG', status: true },
  { name: 'LUPUS', description: 'THE WOLF', power: 8, damage: 4, element: 'EARTH', img: 'LUPUS.PNG', status: true },
  { name: 'LEO', description: 'THE LION', power: 7, damage: 5, element: 'EARTH', img: 'LEO.PNG', status: true },
  { name: 'AQUILA', description: 'THE EAGLE', power: 9, damage: 4, element: 'WIND', img: 'AQUILA.PNG', status: true },
  { name: 'TAURUS', description: 'THE BULL', power: 9, damage: 4, element: 'EARTH', img: 'TAURUS.PNG', status: true },
  { name: 'SCORPIUS', description: 'THE SCORPION', power: 8, damage: 5, element: 'EARTH', img: 'SCORPIUS.PNG', status: true },
  { name: 'HYDRA', description: 'THE WATER SERPENT', power: 8, damage: 5, element: 'WATER', img: 'HYDRA.PNG', status: true },
  { name: 'PHOENIX', description: 'THE FIREBIRD', power: 9, damage: 5, element: 'FIRE', img: 'PHOENIX.PNG', status: true },
  { name: 'COLUMBA', description: 'THE DOVE', power: 9, damage: 5, element: 'WIND', img: 'COLUMBA.PNG', status: true },
  { name: 'AQUARIUS', description: 'THE WATER BEARER', power: 9, damage: 5, element: 'WATER', img: 'AQUARIUS.PNG', status: true },
  { name: 'CENTAURUS', description: 'THE CENTAUR', power: 10, damage: 5, element: 'EARTH', img: 'CENTAURUS.PNG', status: true },
  { name: 'HERCULES', description: 'THE STRONG MAN', power: 9, damage: 5, element: 'EARTH', img: 'HERCULES.PNG', status: true },
  { name: 'ZEUS', description: 'THE THUNDER GOD', power: 10, damage: 5, element: 'WIND', img: 'ZEUS.PNG', status: true }
];

// Create the levels of the game

$(function () {
  // Bind events and initialize plugin
  $('.explode')
    .on('pixellate-exploded', function () {
      var self = this;
      setTimeout(function () {
        $(self).pixellate('in');
      }, 500);
    })
    .on('pixellate-imploded', function () {
      var self = this;
      setTimeout(function () {
        $(self).pixellate('out');
      }, 500);
    })
    .pixellate()
});

var pluginName = 'pixellate',
  defaults = {
    // Grid divisions
    columns: 10,
    rows: 10,

    // Duration of explosion animation
    duration: 1000,

    // Direction of explosion animation ('out', 'in', or 'none')
    direction: 'out',

    // Resize pixels during animation
    scale: true,

    // Coordinates representing the source of the explosion force
    //(e.g. [-1, 1] makes the explodey bits go up and to the right)
    explosionOrigin: [0, 0]
  };

function Plugin(el, options) {
  this.$el = $(el);
  this.options = $.extend({}, defaults, options);
  this._defaults = defaults;
  this._name = pluginName;

  this.init();
};

Plugin.prototype = {
  init: function () {
    if (!this.$el.find('.pixellate-pixel').length) {
      var $img = this.$el.find('img:first-child'),
        img = new Image();

      this.$el
        .data('pixellate-image', $img.attr('src'))
        .addClass('pixellate-lock');
      $img.css('visibility', 'hidden');

      $(img).one('load', $.proxy(this.createPixels, this));

      img.src = this.$el.data('pixellate-image');
      if (img.complete) $(img).trigger('load');
    } else {
      this.stylePixels();
    }
  },

  createPixels: function () {
    this.$el.append(new Array((this.options.rows * this.options.columns) + 1).join(''));

    this.stylePixels(true);
  },

  stylePixels: function (initializeStyles) {
    var self = this,
      w = this.$el.width(),
      h = this.$el.height(),
      columns = this.options.columns,
      rows = this.options.rows,
      $pixels = this.$el.find('.pixellate-pixel');

    var styles = initializeStyles ? {
      'position': 'absolute',
      'width': (w / columns),
      'height': (h / rows),
      'background-image': 'url(' + this.$el.data('pixellate-image') + ')',
      'background-size': w,
      'backface-visibility': 'hidden'
    } : {};

    for (var idx = 0; idx < $pixels.length; idx++) {
      var pixelStyles = {};

      if (initializeStyles) {
        var x = (idx % columns) * styles.width,
          y = (Math.floor(idx / rows)) * styles.height;

        $.extend(pixelStyles, styles, {
          'left': x,
          'top': y,
          'background-position': (-x) + 'px ' + (-y) + 'px'
        });
      }

      if (self.options.direction == 'out') {
        var randX = (Math.random() * 300) - 150 - (self.options.explosionOrigin[0] * 150),
          randY = (Math.random() * 300) - 150 - (self.options.explosionOrigin[1] * 150);

        var transformString = 'translate(' + randX + 'px, ' + randY + 'px)';
        if (self.options.scale) {
          transformString += ' scale(' + (Math.random() * 2.0 + 0.2) + ')';
        }

        $.extend(pixelStyles, {
          'transform': transformString,
          'opacity': 0,
          'transition': self.options.duration + 'ms ease-out'
        });
      } else if (self.options.direction == 'in') {
        $.extend(pixelStyles, {
          'transform': 'none',
          'opacity': 1,
          'transition': self.options.duration + 'ms ease-in-out'
        });
      }

      $pixels.eq(idx).css(pixelStyles);
    }

    // Use rAF to ensure styles are set before class is modified
    requestAnimationFrame(function () {
      if (self.options.direction == 'out') {
        self.$el.removeClass('pixellate-lock');
      } else if (self.options.direction == 'in') {
        self.$el.one('pixellate-imploded', function () {
          self.$el.addClass('pixellate-lock');
        });
      }
    });

    // Fire plugin events after animation completes
    // TODO: Use transition events when supported
    setTimeout(function () {
      if (self.options.direction == 'out')
        self.$el.trigger('pixellate-exploded');
      else if (self.options.direction == 'in')
        self.$el.trigger('pixellate-imploded');
    }, this.options.duration);
  }
};

$.fn[pluginName] = function (options) {
  return this.each(function () {
    if (!$.data(this, "plugin_" + pluginName)) {
      $.data(this, "plugin_" + pluginName, new Plugin(this, options));
    } else if (typeof options === 'string') {
      $.data(this, "plugin_" + pluginName).options.direction = options;
      $.data(this, "plugin_" + pluginName).init();
    }
  });
};

// requestAnimationFrame polyfill by <NAME>. fixes from <NAME> and <NAME>
// MIT license
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
  window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
  window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
}

if (!window.requestAnimationFrame)
  window.requestAnimationFrame = function (callback, element) {
    var currTime = new Date().getTime();
    var timeToCall = Math.max(0, 16 - (currTime - lastTime));
    var id = window.setTimeout(function () { callback(currTime + timeToCall); },
      timeToCall);
    lastTime = currTime + timeToCall;
    return id;
  };

if (!window.cancelAnimationFrame)
  window.cancelAnimationFrame = function (id) {
    clearTimeout(id);
  };

<file_sep>/README.md
# cards-of-war
Greek Mythology Card Game

branch_name: refs/heads/master

languages: Markdown JavaScript

repo_language: Markdown


text:
<repo_name>andreybabynin/td<file_sep>/simplified/replay2.py
import numpy as np
from random import choice

class SimpleReplay: # need to decouple with agent object
    def __init__(self, capacity):
        self.buffer = []
        self.capacity = capacity

    def push(self, step, a, r):
        self.buffer.append((step, a, r))
        if len(self.buffer)>self.capacity:
            self.buffer = self.buffer[-self.capacity:]

    def sample_step(self):
        t = choice(self.buffer[:-1]) #to escape choosing the last value and overcounting events
        return  t[0], t[1], t[2]

class HindsightReplay:
    '''
    https://towardsdatascience.com/reinforcement-learning-with-hindsight-experience-replay-1fee5704f2f8
    https://github.com/orrivlin/Hindsight-Experience-Replay---Bit-Flipping
    https://arxiv.org/pdf/1707.01495.pdf
    '''
    def __init__(self, capacity):
        self.buffer = []
        self.capacity = capacity

class PrioritizedReplay(SimpleReplay):
    '''
    https://medium.freecodecamp.org/improvements-in-deep-q-learning-dueling-double-dqn-prioritized-experience-replay-and-fixed-58b130cc5682
    https://gist.github.com/simoninithomas/d6adc6edb0a7f37d6323a5e3d2ab72ec#file-dueling-deep-q-learning-with-doom-double-dqns-and-prioritized-experience-replay-ipynb
    Idea:
        1) add weights to each replay according to the loss (td - difference)
        2) update these weights if use sample many times to reduce the probability of being chosen next time
    '''

    def __init__(self, capacity, alpha = 0.4, beta = 0.7):
        super().__init__(capacity)
        self.priority_buffer = []
        self.alpha = alpha # randomness coefficient (0 - pure random, 1 - highest priority)
        self.beta = beta

    ''' Override metohds of simple replay'''
    def push(self, step, loss):
        self.buffer.append(step)
        self.priority_buffer.append(loss)
        if len(self.buffer)>self.capacity:
            self.buffer = self.buffer[-self.capacity:]
            self.priority_buffer = self.priority_buffer[-self.capacity:]

    def normalization(self):
        return [np.power(np.abs(loss), self.alpha)/np.sum(np.power(np.abs(self.priority_buffer[:-1]), self.alpha)) for loss in self.priority_buffer[:-1]]

    def sample_step(self):
        step = np.random.choice(self.buffer[:-1],
                                p = np.asarray(self.normalization()).ravel())
        step_index = self.buffer.index(step)
        # Importance sampling weights
        self.priority_buffer[step_index] = np.power(1/(self.capacity *\
                                self.priority_buffer[step_index]), self.beta)
        return step
<file_sep>/env.py
# -*- coding: utf-8 -*-
"""
Created on Wed Jan 23 11:37:36 2019

@author: andrey.babynin
"""

#import pandas as pd
#import numpy as np
import logging

class Environment:
    '''
    Parameters:
        1) slipage - deviation from current price while executing (%)
        2) comission - comission per trade (%)
        3) endowment - initial amount of money
        4) step_number - number of steps in current game
        5) gamma - discount factor
        6) action_space_next - available spaces for trader during next period:
            0/1 - unavailable/available
            action[long, short, hold, close, cash] - list of actions
        7) reward[abs, rel] - reward absolute  ($) and relative (return %) during one game
        8) cum_reward - cumulative reward per game
        11) position - current dollar value of the position
        12) position_size - % size of position relative to available cash

    '''
    def __init__(self, slipage=0.005, comission=0.005, gamma=0.0001, endowment=10**6, position_size=0.01):
        self.slipage = slipage
        self.comission = comission
        self.step_number = 0
        self.gamma = gamma
        self.endowment = endowment
        self.cash = endowment
        self.action_space_next = [0,1,4] # initial state open
        self.action_mask = [1,1,0,0,1]
        self.position = {'abs': 0, 'volume': 0, 'type': '', 'invested':0, 'initial': 0 }
        self.position_size = position_size
        self.history = {'position': [], 'actions': [], 'reward': [], 'syn reward': [],
                        'portfolio': [], 'volume': [], 'steps': [], 'done': [],
                        'cash': [], 'holding period': [], 'open': [], 'close': []}
        logger = logging.getLogger('agent.env')
        logger.info('Endowment is {}'.format(endowment))

    @staticmethod
    def position_value(volume, initial_price, share_price, type_position):
        return (share_price - initial_price)*volume if type_position == 'long' else (initial_price - share_price)*volume

    @staticmethod
    def price_with_com(price, slipage, position, close=False):
        if close==True:
            if position=='long':
                actual_price = (1-slipage)*price
            else:
                actual_price = (1+slipage)*price
        else:
            if position=='long':
                actual_price = (1+slipage)*price
            else:
                actual_price = (1-slipage)*price
        return actual_price

    def step(self, action, share_price):

        self.action_space_next, self.action_mask = self.action_space(action)
        self.r_cash = self.reward(action, share_price)

        '''
        If go long/short
        '''
        if (action == 0) or (action ==1):

            '''
            Slipage mechanics: higher price when goes long, lower otherwise
            '''
            self.position['type'] = 'long' if action==0 else 'short'
            #print(self.position['type'])
            self.position['invested'] = self.cash*self.position_size*(1-self.comission)
            self.position['initial'] = self.price_with_com(share_price, self.slipage,
                         self.position['type'])

            self.position['volume'] = self.position['invested']/self.position['initial']

            self.position['abs'] = self.position_value(self.position['volume'],
                         self.position['initial'], share_price, self.position['type'])+self.position['invested']

            self.cash = self.cash*(1 - self.position_size)

        elif action == 2:
            '''
            Recalculate position while holding
            '''
            self.position['abs'] = self.position_value(self.position['volume'],
                         self.position['initial'], share_price, self.position['type']) +self.position['invested']

        # recalculate while closing position
        elif action == 3:
            self.cash = self.cash + (self.r_cash+1)/(1-self.comission)*self.position['invested']

            self.position = {'abs': 0, 'volume': 0, 'type': '', 'invested':0, 'initial':0}
        # while holding cash
        self.step_number +=1
        '''Append history'''
        self.history['position'].append(self.position['abs'])
        self.history['volume'].append(self.position['volume'])
        self.history['actions'].append(action)
        self.history['holding period'].append(self.holding_period())
        self.history['reward'].append(self.r_cash)
        #self.history['syn reward'].append(self.r)
        self.history['portfolio'].append(self.portfolio_value())
        self.history['steps'] = self.step_number
        self.history['cash'].append(self.cash)

        return self.position, self.r_cash, self.action_space_next

    def portfolio_value(self):
        return self.cash + self.position['abs']

    def holding_period(self):
        if self.history['actions'][-1] in (0,1):
            self.history['open'].append(len(self.history['actions']))
        if self.history['actions'][-1] == 3:
            self.history['close'].append(len(self.history['actions']))
            return self.history['close'][-1] - self.history['open'][-1]
        return 0

    @staticmethod
    def action_space(action):
        action_space_n = [2,3] if action<=2 else [0,1,4]
        action_mask_n = [0,0,1,1,0] if action<=2 else [1,1,0,0,1]
        return action_space_n, action_mask_n

    def reward(self, action, share_price):
        '''Reward is the difference between portfolio value between two time steps'''
        if action==3:
            reward_cash = ((self.position_value(self.position['volume'],
                         self.position['initial'], self.price_with_com(share_price,
                                      self.slipage, self.position['type'], close=True),
                         self.position['type']) +\
                        self.position['invested'])*(1-\
                        self.comission))/(self.position['invested']/(1-self.comission))-1

        #elif action ==4:
        #    reward = -0.005 # hate cash
        #    reward_cash = 0
        #elif action ==2:
        #    reward = 0 # take more actions
        #    reward_cash = 0
        else:
            #reward = 0
            reward_cash = 0
        return reward_cash

    def reset(self):
        self.history = {}
        self.cash = self.endowment
    <file_sep>/simplified/agent2.py
from settings import GAMMA
import numpy as np
import torch

class DQN:
    def __init__(self, engine, explore_start=0.3, explore_stop=0.001,
                 decay_rate=0.000001):
        self.engine = engine # NN architecture
        self.explore_start = explore_start
        self.explore_stop = explore_stop
        self.decay_rate = decay_rate
        self._step = 0  #global step at the training
        self._lstm = engine.__class__.__name__ == 'DuelingLSTM'
        self._dueling = engine.__class__.__name__ == 'DuelingQnet'

    def explore_prob(self):
        return self.explore_stop + (self.explore_start -\
                self.explore_stop)*np.exp(-self.decay_rate *self._step)

    def get_q_values(self, step, space, metrics = None):
        return self.dueling_q(step, space, metrics) if self._dueling else self.simple_q(step, space, metrics)

    def dueling_q(self, step, space, metrics = None):
        Qs = torch.empty(0)
        adv_proxy = torch.empty(0)
        for a in space:
            action_vector = np.zeros([5])
            if self._lstm:
                pass
            else:
                action_vector[a]+=1
                v = np.append(metrics.events_hist[step, :], action_vector)
                for n in metrics.ret.keys():
                    v = np.append(v, metrics.ret[n][step])
                s, adv = self.engine(torch.Tensor(v))
                Q_proxy = s + adv
                adv_proxy = torch.cat((adv_proxy, adv), -1)
                Qs = torch.cat((Qs, Q_proxy), -1)
        return Qs - adv_proxy.mean()

    def simple_q(self, step, space, metrics = None):
        Qs = torch.empty(0)
        for a in space:
            action_vector = np.zeros([5])
            if self._lstm:
                pass
            else:
                action_vector[a]+=1
                v = np.append(metrics.events_hist[step, :], action_vector)
                for n in metrics.ret.keys():
                    v = np.append(v, metrics.ret[n][step])
                Q = self.engine(torch.Tensor(v))
                Qs = torch.cat((Qs, Q), -1)
        return Qs

    def get_next_q_value(self, step, action, env = None, metrics= None):
        space = env.action_space(action)
        return self.get_q_values(step, space, metrics).max()

'''Double Q learning update target weights '''
def update_target(current_model, target_model):
    target_model.engine.load_state_dict(current_model.engine.state_dict())

def td_loss(current_model, step, action, reward, target_model = None, env = None, metrics= None):

    if target_model != None:
        next_qvalue = target_model.get_next_q_value(step, action, env, metrics).detach()
    else:
        next_qvalue = current_model.get_next_q_value(step, action, env, metrics).detach()

    expected_q_value  = torch.Tensor(np.array([reward])) + next_qvalue
    loss = (current_model.qvalues - GAMMA*torch.max(expected_q_value)).pow(2).mean()
    return loss

def play_step(agent, step, env = None, metrics = None):

    agent._step += 1 #internal step calculator in order to compute exploration probability checked
    # Need to calculate q_values even if taking random action in order to compute td_loss
    agent.qvalues = agent.get_q_values(step, env.action_space_next, metrics)
    exp_exp_tradeoff = np.random.rand()
    explore_probability = agent.explore_prob()
    if step != metrics._len-1:
        if (explore_probability > exp_exp_tradeoff):
            action = np.random.choice(env.action_space_next)
        else:
            action = env.action_space_next[np.argmax(agent.qvalues.detach().numpy())]
    else:
        action = max(env.action_space_next) # close any positions at the end of epoch 3 - close, 4 -do nothing

    env.step(action, step, metrics)
    return action
<file_sep>/simplified/env2.py
class Environment:
    '''
    Parameters:
        1) slipage - deviation from current price while executing (%)
        2) comission - comission per trade (%)
        3) endowment - initial amount of money
        6) action_space_next - available spaces for trader during next period:
            0/1 - unavailable/available
            action[long, short, hold, close, cash] - list of actions
        7) reward - reward %
        11) position - current dollar value of the position
        12) position_size - % size of position relative to available cash

    '''
    def __init__(self, slipage=0.005, comission=0.005, endowment=10**6, position_size=0.01):
        self.slipage = slipage
        self.comission = comission
        self.endowment = endowment
        self.cash = endowment
        self.action_space_next = [0,1,4] # initial state open
        self.position = {'abs': 0, 'volume': 0, 'type': '', 'invested':0, 'initial': 0 }
        self.position_size = position_size
        self.history = {'position': [], 'actions': [], 'reward': [],
                        'portfolio': [], 'volume': [],
                        'cash': [], 'holding period': [], 'open': [], 'close': []}

    def step(self, action, step, metrics=None):
        '''
        step - number of step
        actions:
            0 - long
            1 - short
            2 -hold
            3 - close
            4 - nothing
        '''

        share_price = metrics.history[step+metrics._max, metrics._close_cl]
        self.action_space_next = self.action_space(action)
        self.r_cash = self.reward(action, share_price)
        '''
        If go long/short
        '''
        if (action == 0) or (action ==1):

            '''
            Slipage mechanics: higher price when goes long, lower when go short
            '''
            self.position['type'] = 'long' if action==0 else 'short'

            self.position['invested'] = self.cash*self.position_size*(1-self.comission)
            self.position['initial'] = self.price_with_com(share_price, self.slipage,
                         self.position['type'])

            self.position['volume'] = self.position['invested']/self.position['initial']

            self.position['abs'] = self.position_value(self.position['volume'],
                         self.position['initial'], share_price, self.position['type'])+self.position['invested']

            self.cash = self.cash*(1 - self.position_size)

        elif action == 2:
            '''
            Recalculate position while holding
            '''
            self.position['abs'] = self.position_value(self.position['volume'],
                         self.position['initial'], share_price, self.position['type']) +self.position['invested']

        elif action == 3:
            self.cash = self.cash + (self.r_cash+1)/(1-self.comission)*self.position['invested']

            self.position = {'abs': 0, 'volume': 0, 'type': '', 'invested':0, 'initial':0}

        self.history['position'].append(self.position['abs'])
        self.history['volume'].append(self.position['volume'])
        self.history['actions'].append(action)
        self.history['holding period'].append(self.holding_period())
        self.history['reward'].append(self.r_cash)
        self.history['portfolio'].append(self.cash + self.position['abs'])
        self.history['cash'].append(self.cash)

    def holding_period(self):
        if self.history['actions'][-1] in (0,1):
            self.history['open'].append(len(self.history['actions']))
        if self.history['actions'][-1] == 3:
            self.history['close'].append(len(self.history['actions']))
            return self.history['close'][-1] - self.history['open'][-1]
        return 0

    def reward(self, action, share_price):
        '''Reward %'''
        if action==3:
            reward_cash = ((self.position_value(self.position['volume'], self.position['initial'],
                                    self.price_with_com(share_price, self.slipage, self.position['type'], close=True),
                                    self.position['type']) + self.position['invested'])*(1-\
                        self.comission))/(self.position['invested']/(1-self.comission))-1
        else:
            reward_cash = 0
        return reward_cash

    @staticmethod
    def position_value(volume, initial_price, share_price, type_position):
        return (share_price - initial_price)*volume if type_position == 'long' else (initial_price - share_price)*volume

    @staticmethod
    def price_with_com(price, slipage, position, close=False):
        if close==True:
            return (1-slipage)*price if position=='long' else (1+slipage)*price
        else:
            return (1+slipage)*price if position=='long' else (1-slipage)*price
    @staticmethod
    def action_space(action):
        return [2,3] if action<=2 else [0,1,4]
<file_sep>/Qnet.py
# -*- coding: utf-8 -*-
"""
Created on Tue Feb 26 17:13:57 2019

@author: andrey.babynin
"""

import torch
import torch.nn as nn
import torch.nn.functional as F

class Qnet(nn.Module):
    def __init__(self, layers = []): # first and last elements - unput shape and output shape
        super(Qnet, self).__init__()
        self.layer1  = nn.Linear(layers[0],layers[1])
        self.layer2 = nn.Linear(layers[1],layers[2])
        self.layer3 = nn.Linear(layers[2], layers[3])

    def forward(self, x):
        h = F.elu(self.layer1(x))
        h = F.elu(self.layer2(h))
        #h = F.tanh(self.layer3(h))
        h = self.layer3(h)
        return h

class DuelingQnet(nn.Module):
    def __init__(self, layers= []):
        super(DuelingQnet, self).__init__()
        self.layer1 = nn.Linear(layers[0],layers[1])
        self.layer2 = nn.Linear(layers[1],layers[2])
        '''layer for value state'''
        self.state = nn.Linear(layers[2], layers[3])
        ''' layer for advantage value '''
        self.adv = nn.Linear(layers[2], layers[3])

    def forward(self, x):
        h = F.elu(self.layer1(x))
        h = F.elu(self.layer2(h))
        #h = F.tanh(self.layer3(h))
        state = self.state(h)
        advantage = self.adv(h)
        return state, advantage

class DuelingLSTM(nn.Module):
    def __init__(self,  input_size, hidden_size, seq_length = 3,
                 layers=1, batch_size=1, bidirectional=False, lin_layers = []):
        super(DuelingLSTM, self).__init__()
        directions = 2 if bidirectional else 1
        self._seq_length = seq_length
        self._batch_size = batch_size

        self.lstm = nn.LSTM(input_size, hidden_size, num_layers = layers)
        self.h_0 = torch.randn((directions*layers, batch_size, hidden_size)) #initialize hidden state
        self.c_0 = torch.randn((directions*layers, batch_size, hidden_size)) #initialize cell state
        self.linear1 = nn.Linear(hidden_size*self._seq_length, lin_layers[0])
        ''' value state '''
        self.state = nn.Linear(lin_layers[0], lin_layers[1])
        ''' value advantage '''
        self.adv = nn.Linear(lin_layers[0], lin_layers[1])

    def reshape(self, x):
        return x.reshape(self._seq_length, self._batch_size, -1) # 3 - number of time periods,
    #1 - batch_size, input_size - number of feature

    def forward(self, x):
        x = self.reshape(x)
        x, (self.h_0, self.c_0) = self.lstm(x, (self.h_0.detach(), self.c_0.detach()))
        x = x.view(-1)
        x = F.elu(self.linear1(x))
        state = self.state(x)
        adv = self.adv(x)
        return state, adv

'''
a = np.array([[1,2,3,8], [4,5,6,7], [4,6,8,12]])

b = np.vstack((a, [1,2,3,4]))

b[1:]

agent = DuelingLSTM(4,2, lin_layers = [3,1])

agent.forward(a1)

a = np.array([1,2,3]).reshape(1,-1)
np.repeat(a, 3, 0)

b = np.array([1,2,3]).reshape(1,-1)
b1 = np.array([3,4,5]).reshape(1,-1)

b2 = np.append(b,b1, 0)
b2.shape

b3 = np.array([7,9,1]).reshape(1,-1)
b4 = np.append(b2,b3, 0)
a2 = np.array([20, 30, 40]).reshape(3,-1)
np.append(b4, a2, 1)
'''
'''
Code snippet for number of parameters

def count_parameters(model):
    total_param = 0
    for name, param in model.named_parameters():
        if param.requires_grad:
            num_param = np.prod(param.size())
            if param.dim() > 1:
                print(name, ':', 'x'.join(str(x) for x in list(param.size())), '=', num_param)
            else:
                print(name, ':', num_param)
            total_param += num_param
    return total_param
'''
<file_sep>/utils.py
# -*- coding: utf-8 -*-
"""
Created on Tue Mar  5 17:44:14 2019

@author: andrey.babynin
"""

import pandas as pd
import numpy as np

class Utils:
    def __init__(self):
        pass
    @staticmethod
    def batch_days(x, d):
        """ Split the entire history into batches by days"""
        return d, x['<CLOSE>'].loc[x['<DATE>']==d], x['<VOL>'].loc[x['<DATE>']==d], len(x.loc[x['<DATE>']==d])-1
    #-1 because price next eats one time shift

    @staticmethod
    def extract_time(x):
        date = datetime.datetime.strptime(str(x['<DATE>'])[:8], '%Y%m%d')
        time = datetime.datetime.strptime(str(x['<TIME>'])[:6], '%H%M%S')
        return datetime.datetime.combine(date.date(), time.time())

<file_sep>/simplified/Qnet.py
import torch
import torch.nn as nn
import torch.nn.functional as F

class Qnet(nn.Module):
    def __init__(self, layers = []): # first and last elements - unput shape and output shape
        super(Qnet, self).__init__()
        self.layer1  = nn.Linear(layers[0],layers[1])
        self.layer2 = nn.Linear(layers[1],layers[2])
        self.layer3 = nn.Linear(layers[2], layers[3])

    def forward(self, x):
        h = F.elu(self.layer1(x))
        h = F.elu(self.layer2(h))
        #h = F.tanh(self.layer3(h))
        h = self.layer3(h)
        return h

class DuelingQnet(nn.Module):
    def __init__(self, layers= []):
        super(DuelingQnet, self).__init__()
        self.layer1 = nn.Linear(layers[0],layers[1])
        self.layer2 = nn.Linear(layers[1],layers[2])
        '''layer for value state'''
        self.state = nn.Linear(layers[2], layers[3])
        ''' layer for advantage value '''
        self.adv = nn.Linear(layers[2], layers[3])

    def forward(self, x):
        h = F.elu(self.layer1(x))
        h = F.elu(self.layer2(h))
        #h = F.tanh(self.layer3(h))
        state = self.state(h)
        advantage = self.adv(h)
        return state, advantage

class DuelingLSTM(nn.Module):
    def __init__(self,  input_size, hidden_size, seq_length = 3,
                 layers=1, batch_size=1, bidirectional=False, lin_layers = []):
        super(DuelingLSTM, self).__init__()
        directions = 2 if bidirectional else 1
        self._seq_length = seq_length
        self._batch_size = batch_size

        self.lstm = nn.LSTM(input_size, hidden_size, num_layers = layers)
        self.h_0 = torch.randn((directions*layers, batch_size, hidden_size)) #initialize hidden state
        self.c_0 = torch.randn((directions*layers, batch_size, hidden_size)) #initialize cell state
        self.linear1 = nn.Linear(hidden_size*self._seq_length, lin_layers[0])
        ''' value state '''
        self.state = nn.Linear(lin_layers[0], lin_layers[1])
        ''' value advantage '''
        self.adv = nn.Linear(lin_layers[0], lin_layers[1])

    def reshape(self, x):
        return x.reshape(self._seq_length, self._batch_size, -1) # 3 - number of time periods,
    #1 - batch_size, input_size - number of feature

    def forward(self, x):
        x = self.reshape(x)
        x, (self.h_0, self.c_0) = self.lstm(x, (self.h_0.detach(), self.c_0.detach()))
        x = x.view(-1)
        x = F.elu(self.linear1(x))
        state = self.state(x)
        adv = self.adv(x)
        return state, adv
<file_sep>/agent.py
# -*- coding: utf-8 -*-
"""
Created on Wed Feb 27 15:27:39 2019

@author: andrey.babynin
"""
import pandas as pd
import numpy as np
from env import Environment
from metrics import Metrics
from Qnet import Qnet, DuelingQnet, DuelingLSTM
from utils import Utils
from rewards import Reward
import torch
import torch.optim as optim
import matplotlib.pyplot as plt
from tensorboardX import SummaryWriter
import logging
from time import gmtime, strftime

HISTORY_BATCH = 100 # inintial history to calculate statistics
COMISSION = 0.005 #for opening and closing position
SLIPAGE = 0.005 # deviation from the price at the trading screen
REPLAY_PROB = 0.1 # probabiliy to sample day for replay
REPLAY_BUFFER_SIZE = 1000

def close_at_day(day_step, day_length, action):
    '''Closes position at the end of the day '''
    if day_step == day_length:
        day_step = 0
        done = True
        if action==2 or action ==3:
            action = 3
        else:
            action = 4
            #print('day is closed')
    else:
        action = action
        done = False
    return done, day_step, action

def play_replay(kind = 'simple'):
    ''' Choose replay mode ['simple, 'prioritized'] '''
    if loss > np.mean(loss_list[-100:]): #невно заданные аргменты loss и loss_list потенциальная проблема
        if kind=='simple':
            rb.push(agent._step)
        if kind=='prioritized':
            rb.push(agent._step, loss)

    replay_prob = np.random.rand()
    if replay_prob<REPLAY_PROB and count_days>10: # replay after some training to escape bad states
        step = rb.sample_step()
        rb.td_rb_loss(step)

def create_logs():
    logger = logging.getLogger("agent")
    logger.setLevel(logging.INFO)
    # create the logging file handler
    fh = logging.FileHandler("DQN.log", mode='w')

    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    fh.setFormatter(formatter)

    # add handler to logger object
    logger.addHandler(fh)
    # initial information
    logger.info("Program started at {}".format(strftime("%d_%b_%Y_%H:%M:%S", gmtime())))
    logger.info('Slippage is {0:.3f}, comission is {1:.3f}'.format(SLIPAGE, COMISSION))
    return logger

class DQN:
    def __init__(self, engine, explore_start=0.3, explore_stop=0.001,
                 decay_rate=0.000001, dueling=False, lstm=False):
        #self.env = env # portfolio calculations
        #self.metrics = metrics #transformations of prices
        self.engine = engine # NN architecture
        #self.rev = rev # reward mechanics
        self.explore_start = explore_start
        self.explore_stop = explore_stop
        self.decay_rate = decay_rate
        self._step = 0  #global step at the training
        self.day_step = 0 #step within a day
        self.gamma = 1 # no discounting
        self.done = False
        self.dueling = dueling
        self.lstm = lstm

    def explore_prob(self):
        return self.explore_stop + (self.explore_start -\
                self.explore_stop)*np.exp(-self.decay_rate *self._step)

    def get_q_values(self, price, volume, space):
        states = np.empty([1,32])
        a_h = np.empty([1])
        Qs = torch.empty(0)

        if self.lstm:
            if self._step>self.engine._seq_length:
                for i in range(1,self.engine._seq_length):
                    s = metrics.vector(replay=True, step = self._step-1-i)
                    states = np.vstack((states, s))
                    a_h = np.vstack((a_h, env.history['actions'][self._step-1-i]))

            else:
                states = np.repeat(metrics.step(price, volume),
                                   self.engine._seq_length, 0).reshape(self.engine._seq_length,-1)
        else:
            states = metrics.step(price, volume)

        if self.dueling:
            adv_proxy = torch.empty(0)
            for a in space:
                if self.lstm:
                    if self._step>self.engine._seq_length:
                        states = np.vstack((states, metrics.step(price, volume)))[1:]
                        a = np.vstack((a_h, a))[1:]
                    else:
                        a = np.repeat(a, self.engine._seq_length, 0).reshape(self.engine._seq_length,-1)
                    #print(a.shape)
                    #print(states.shape)
                    v = np.append(states, a, 1)
                else:
                    v = np.append(states, a)
                s, adv = self.engine(torch.Tensor(v))
                Q_proxy = s+adv
                adv_proxy = torch.cat((adv_proxy, adv), -1)
                Qs = torch.cat((Qs, Q_proxy), -1)
            Qs = Qs - adv_proxy.mean()
        else:
            for a in space:
                #v = np.append(states,a,self.env.reward(a, price)[0]])
                v = np.append(states, a)
                Q = self.engine(torch.Tensor(v))
                Qs = torch.cat((Qs, Q), -1)

        return Qs

    def get_next_q_value(self, action, price_next, volume_next):
        space, _ = env.action_space(action)
        return self.get_q_values(price_next, volume_next, space).max()

class SimpleReplay: # need to decouple with agent object
    def __init__(self, capacity, dueling=False):
        self.buffer = []
        self.capacity = capacity
        self.dueling = dueling

    def push(self, step):
        self.buffer.append(step)
        if len(self.buffer)>self.capacity:
            self.buffer = self.buffer[-self.capacity:]

    def sample_step(self):
        return np.random.choice(self.buffer[:-1]) #to escape choosing the last value and overcounting events

    def get_rb_q_value(self, step):
        if agent.lstm:
            v = np.empty([1,33]) #костыль размерности
            for i in range(agent.engine._seq_length):
                v= np.vstack((v,np.append(metrics.vector(replay=True, step = step-1-i),
                     env.history['actions'][step-1-i])))
            v = v[1:]
        else:
            v = np.append(metrics.vector(replay=True, step = step-1),
                      env.history['actions'][step-1])
        if self.dueling:
            s,adv = agent.engine(torch.Tensor(v))
            return s+adv
        else:
            return agent.engine(torch.Tensor(v))

    def get_next_rb_q_value(self, step):
        return self.get_rb_q_value(step)

    def td_rb_loss(self, step):
        Q = self.get_rb_q_value(step)
        next_Q = self.get_next_rb_q_value(step).detach()
        expected_Q = torch.Tensor(np.array(env.history['syn reward'][step-1])) +\
                             agent.gamma*next_Q*(1-env.history['done'][step-1])
        loss = (Q - expected_Q).pow(2).mean()
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

class HindsightReplay:
    '''
    https://towardsdatascience.com/reinforcement-learning-with-hindsight-experience-replay-1fee5704f2f8
    https://github.com/orrivlin/Hindsight-Experience-Replay---Bit-Flipping
    https://arxiv.org/pdf/1707.01495.pdf
    '''
    def __init__(self, capacity):
        self.buffer = []
        self.capacity = capacity

class PrioritizedReplay(SimpleReplay):
    '''
    https://medium.freecodecamp.org/improvements-in-deep-q-learning-dueling-double-dqn-prioritized-experience-replay-and-fixed-58b130cc5682
    https://gist.github.com/simoninithomas/d6adc6edb0a7f37d6323a5e3d2ab72ec#file-dueling-deep-q-learning-with-doom-double-dqns-and-prioritized-experience-replay-ipynb
    Idea:
        1) add weights to each replay according to the loss (td - difference)
        2) update these weights if use sample many times to reduce the probability of being chosen next time
    '''

    def __init__(self, capacity, alpha = 0.4, beta = 0.7, dueling=False):
        super().__init__(capacity, dueling)
        self.priority_buffer = []
        self.alpha = alpha # randomness coefficient (0 - pure random, 1 - highest priority)
        self.beta = beta

    ''' Override metohds of simple replay'''
    def push(self, step, loss):
        self.buffer.append(step)
        self.priority_buffer.append(loss)
        if len(self.buffer)>self.capacity:
            self.buffer = self.buffer[-self.capacity:]
            self.priority_buffer = self.priority_buffer[-self.capacity:]

    def normalization(self):
        return [np.power(np.abs(loss), self.alpha)/np.sum(np.power(np.abs(self.priority_buffer[:-1]), self.alpha)) for loss in self.priority_buffer[:-1]]

    def sample_step(self):
        #print('sum of probs', np.sum(preprocessing.normalize(np.reshape(np.array(self.priority_buffer), (1,-1))).ravel()))
        #print(preprocessing.normalize(np.reshape(np.array(self.priority_buffer), (1,-1))).ravel())

        step = np.random.choice(self.buffer[:-1],
                                p = np.asarray(self.normalization()).ravel())
        step_index = self.buffer.index(step)
        # Importance sampling weights
        self.priority_buffer[step_index] = np.power(1/(self.capacity *\
                                self.priority_buffer[step_index]), self.beta)
        return step

'''Double Q learning update weights '''
def update_target(current_model, target_model):
    target_model.engine.load_state_dict(current_model.engine.state_dict())

def td_loss(cum_loss, price_next, volume_next, action, reward,  current_model,
                target_model = None, double = False):
    if double:
        next_qvalue = target_model.get_next_q_value(action, price_next,
                                                 volume_next).detach()
    else:
        next_qvalue = current_model.get_next_q_value(action, price_next,
                                                 volume_next).detach()

    expected_q_value  = torch.Tensor(np.array([reward])) +\
                             current_model.gamma*next_qvalue*(1-current_model.done)
    loss = (current_model.qvalues - expected_q_value).pow(2).mean()

    '''Accumulation of losses, update weights periodically: every 50th step'''
    cum_loss +=loss
    if agent._step % 50==0:
        optimizer.zero_grad()
        cum_loss.backward() #https://jdhao.github.io/2017/11/12/pytorch-computation-graph/
        optimizer.step()
        cum_loss = torch.tensor(0, dtype=torch.float)

    return loss, cum_loss

def play_step(price, volume, day_length): # неявно заданные объект agent
    agent.done = False
    agent._step += 1
    agent.day_step +=1
    # Need to calculate q_values even if taking random action in order to compute td_loss

    agent.qvalues = agent.get_q_values(price, volume, env.action_space_next)
    # Probability to choose action randomly
    exp_exp_tradeoff = np.random.rand()
    explore_probability = agent.explore_prob()
    if (explore_probability > exp_exp_tradeoff):
            # Make a random action (exploration)
        action = np.random.choice(env.action_space_next)
    else:
            # Take the biggest Q value (= the best action)
        action = env.action_space_next[np.argmax(agent.qvalues.detach().numpy())]

    agent.done, agent.day_step, action = close_at_day(agent.day_step,
                                                             day_length, action)
    env.history['done'].append(int(agent.done))
        # add values to history
    metrics.add_to_history(price, volume)
        # recalculate position after executing action
    env.step(action, price)
        # add syntetic reward
    reward = rev.multi_reward(env.r_cash, agent._step,
                                       action, mode=False)
    env.history['syn reward'].append(reward)
    return action, explore_probability, reward

if __name__ == "__main__":

    logger = create_logs()
    raw_data = pd.read_csv('GAZP_180217_190217.csv', header = 0, sep =',')
    u = Utils()
    #agent = DQN(Qnet(layers = [33,20,15,1]))  # +1 for action
    #target_agent = DQN(Qnet(layers = [33,20,15,1]))

    #agent = DQN(DuelingQnet(layers = [33,20,15,1]), dueling=True)
    #target_agent = DQN(DuelingQnet(layers = [33,20,15,1]), dueling=True)

    agent = DQN(DuelingLSTM(33,10, lin_layers = [15,1]), dueling = True, lstm=True)
    target_agent = DQN(DuelingLSTM(33,10, lin_layers = [15,1]), dueling = True, lstm = True)

    rb = SimpleReplay(REPLAY_BUFFER_SIZE, dueling=True)
    #rb = PrioritizedReplay(REPLAY_BUFFER_SIZE, dueling=True)
    env = Environment(comission = COMISSION, slipage = SLIPAGE)
    metrics = Metrics(raw_data['<CLOSE>'][:HISTORY_BATCH].values,
                  raw_data['<VOL>'][:HISTORY_BATCH].values, [14,30,45])
    rev = Reward()

    optimizer = optim.Adam(agent.engine.parameters()) # initialize optimizer
    writer = SummaryWriter(comment = '_Dueling_LSTM') # tensorboardX

    loss_list = []
    reward_list = []
    count_days = 0
    dates = sorted(set(raw_data['<DATE>'][HISTORY_BATCH:]))
    double = True # double Q-learning
    cum_loss = torch.tensor(0, dtype = torch.float)
    for d in dates:
        count_days+=1
        date, days_prices, days_volume, days_length = u.batch_days(raw_data[HISTORY_BATCH:], d)
        #print('Next day is :', date, 'number of observations:', days_length)
        logger.info('Next day is {0}, number of observations {1}'.format(date,days_length))
        for price, price_next, volume, volume_next in zip(days_prices[:-1].values,
                                                          days_prices[1:].values,
                                                          days_volume[:-1].values,
                                                          days_volume[1:].values):
            a, pr, r = play_step(price, volume, days_length)
            reward_list.append(r)

            loss, cum_loss = td_loss(cum_loss, price_next, volume_next, a, r, agent, target_agent,
                             double=double)

            loss_list.append(loss.detach().item())
            play_replay(kind= 'simple')

            if agent._step % 100 == 0:
                  ''' Update weights of target mode'''
                  if double:
                      update_target(agent, target_agent)
                  # write to tensorboard
                  writer.add_scalar('reward', np.mean(reward_list[-100:]), agent._step)
                  writer.add_scalar('loss', np.mean(loss_list[-100:]), agent._step)
                  print('Loss is {}'.format(np.mean(loss_list[-100:])))
                  print('Reward is {}'.format(np.mean(reward_list[-100:])))
                  #print('Explore probability is {}'.format(pr))

    writer.close()

actions = env.history['actions']
portfolio = env.history['portfolio']
holding = env.history['holding period']
open = env.history['open']
close = env.history['close']
cash = env.history['cash']
reward = env.history['reward']
syneward = env.history['syn reward']
history = metrics.close
done = env.history['done']

def plot_graph(var):
    plt.clf()
    _ = plt.plot(var)
    plt.show()

plot_graph(portfolio)
plot_graph(holding)
plot_graph(reward)
plot_graph(actions)

plt.clf()
_ = plt.plot(portfolio)
#_  = plt.plot(history[100:], c = 'g')
plt.show()
'''

'''
for handler in logging.root.handlers[:]:
    logging.root.removeHandler(handler)
'''

<file_sep>/rewards.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Mar 14 19:21:13 2019

@author: enron07
"""
import numpy as np

class Reward:

    def __init__(self):
        pass

    def multi_reward(self, r_cash, step, action,  mode=True):
        '''Multi reward scheme'''
        if mode:
            return (1/step)*self.hate_cash() + self.holding_impatience(step, action) +\
            r_cash*np.log(step)
        else:
            return r_cash

    def hate_cash(self, mode = True, hate = -0.05):
        '''Penalty for staying in cash'''
        if mode:
            return hate
        else:
            return 0

    def holding_impatience(self, step, action, mode = True, impatience = -0.02, hold_min=6, hold_max = 30): #2,5 hours and 30 min
        '''Penalty for very long positions'''
        if mode:
            if action ==2:
                return impatience/np.log(step)
            else: return 0
        else:
            return 0

    def price_direction(self):
        '''Reward for guessing riht price direction and taking appropriate long/short positions'''
        pass

<file_sep>/simplified/settings.py
COMISSION = 0.0 #for opening and closing position
SLIPAGE = 0.0 # deviation from the price at the trading screen
REPLAY_PROB = 0.2 # probabiliy to sample day for replay
TRAINING = True # training mode
REPLAY_BUFFER_SIZE = 1000
LAYERS = [14,8,5,1] #initial layer estimations*levels +5 (actions)
DUELING = True
DOUBLE = True
GAMMA = 0.999
<file_sep>/simplified/main.py
import numpy as np
from settings import COMISSION, SLIPAGE, REPLAY_PROB, TRAINING, REPLAY_BUFFER_SIZE, LAYERS, DUELING, DOUBLE
from Qnet import Qnet, DuelingQnet
from replay2 import SimpleReplay
from agent2 import DQN, play_step, td_loss, update_target
from rewards2 import Reward
import torch.optim as optim
from tensorboardX import SummaryWriter
from env2 import Environment
from metrics2 import Metrics
import pandas as pd
f = pd.read_csv('GAZP_180217_190217.csv', header = 0, sep =',')

if __name__ == "__main__":

    engine = DuelingQnet(layers = LAYERS) if DUELING else Qnet(layers = LAYERS)
    agent = DQN(engine)
    target_agent = DQN(engine) if DOUBLE else None

    metrics = Metrics(f, 'support', 'resistance')
    metrics.eda()

    env = Environment(slipage=SLIPAGE, comission=COMISSION)
    rb = SimpleReplay(REPLAY_BUFFER_SIZE)
    rev = Reward()

    optimizer = optim.Adam(agent.engine.parameters())
    writer = SummaryWriter(comment = '5 epoch Double dueling Q learning no comissions gamma')

    loss_list = []
    reward_list = []

    def optimizer_step(loss):
        optimizer.zero_grad()
        loss.backward() #https://jdhao.github.io/2017/11/12/pytorch-computation-graph/
        optimizer.step()

    for epoch in range(2):
        print('Epoch {} started'.format(epoch))
        for step in range(metrics._len-metrics._max):
            a = play_step(agent, step, env, metrics)
            r = rev.simple_reward(agent._step-1, env)

            reward_list.append(r)
            if TRAINING:
                loss = td_loss(agent, step, a, r, target_agent, env, metrics)
                optimizer_step(loss)
                loss_list.append(loss.detach().item())

                if loss > np.mean(loss_list[-100:]):
                    rb.push(step, a, r)
                ''' learning from replay '''
                if np.random.rand() < REPLAY_PROB and step > 500:
                    step, a, r = rb.sample_step()
                    agent.qvalues = agent.get_q_values(step, env.action_space(a), metrics)
                    loss = td_loss(agent, step, a, r, target_agent, env, metrics)
                    optimizer_step(loss)
                ''' update target function for double learning'''
                if agent._step % 100 == 0:
                    if target_agent != None:
                        update_target(agent, target_agent)

                    writer.add_scalar('reward', np.mean(reward_list[-100:]), agent._step)
                    writer.add_scalar('loss', np.mean(loss_list[-100:]), agent._step)
                    print('Loss is {}'.format(np.mean(loss_list[-100:])))
                    print('Reward is {}'.format(np.mean(reward_list[-100:])))

    writer.close()
<file_sep>/metrics.py
# -*- coding: utf-8 -*-
"""
Created on Mon Feb 25 15:39:13 2019

@author: andrey.babynin
"""

import pandas as pd
import numpy as np

'''
Metrics

highest values last n ticks
lowest values last n ticks
price crosess highest/lowest values (support resistance)
ema last n ticks
ema m crosses ema n
price relative to lowest / highest values
'''
'''
raw_data = pd.read_csv('GAZP_180101_181101_5min.csv', header = 0, sep =';')

close_data = raw_data['<CLOSE>']
'''
class Metrics:

    def __init__(self, history):

        self.history = history # all history of prices
        self.levels = {'resistance': {}, 'support': {}, 'price return': {},
                       'ema': {}, 'volatility': {}
                       } # dictionary of eda statististics
        self.events = {'cross_resistance': {}, 'cross_support': {},
                       'cross ema from below': {},
                       'cross ema from above': {},
                       'price increase': 0
                        } # dictionary of statististics

    def eda(self, levels, axis = 0):
        for l in levels:
            self.levels['resistance'][l] = self.history[:-l].max()
            self.levels['support'][l] = self.history[:-l].min()
            self.levels['ema'][l] = np.ma.average(self.history[-l:], axis=0,
                       weights=range(l))

            self.levels['price return'][l] = self.history[-1]/self.history[-l]-1
            self.levels['volatility'][l] = np.std(self.history[-l:])

    def my_events(self, price):
        self.events['cross_resistance'] = {k: price>i for k,i in self.levels['resistance'].items()}
        self.events['cross_support'] = {k: price<i for k,i in self.levels['support'].items()}

        self.events['cross ema from below'] = {k: 1 if price>=i and self.history[-1]<i
                   else 0 for k,i in self.levels['ema'].items()}
        self.events['cross ema from above'] = {k: 1 if price<=i and self.history[-1]>i
                   else 0 for k,i in self.levels['ema'].items()}
        self.events['price increase'] = 1 if price>self.history[-1] else 0

    def _flatten(self, dics):
        return np.fromiter(dics.values(), dtype = float)

    def vector(self):
        arr = np.array([])
        for event in self.events.values():
            try:
                arr = np.append(arr, self._flatten(event))
            except:
                arr = np.append(arr, event)
        return arr

    def step(self, price, levels):
        self.eda(levels)
        self.my_events(price)
        return self.vector()

    def add_to_history(self, price):
        self.history = np.append(self.history, price)

    def delete_history(self):
        self.history = self.history[:-1]

'''
a = close_data.values

m = Metrics(a[:100])
for i in a[100:200]:
    m.eda(levels = [10,20,50])
    m.my_events(i)
    a1 = m.vector()

    Neural network starts from here

    m.step(i)

'''

<file_sep>/simplified/metrcis2.py
import numpy as np

class Metrics:

    def __init__(self, history, *args, training=True):
        '''
            history: df
        '''
        self._training = training
        self.history = np.array(history)
        self._len = self.history.shape[0]
        self._define_col(history)
        self.define_inst(*args)
        self.returns(1,5,10)

    def _define_col(self, history):
        self._close_cl = history.columns.get_loc('<CLOSE>')
        self._volume_cl = history.columns.get_loc('<VOL>')

    def define_inst(self, *args):
        self._instruments = []
        self._events = []
        dic = {'resistance': self._resistance, 'support': self._support}
        dic_e = {'resistance': self._resistance_break, 'support': self._support_break}
        for inst in args:
            if inst in dic.keys():
                self._instruments.append(dic[inst])
                self._events.append(dic_e[inst])
        self._n_inst = len(self._instruments)

    ''' Library of functions '''

    def _resistance(self, l, step):
        return self.history[step+self._max-l: step+self._max, self._close_cl].max()

    def _support(self, l, step):
        return self.history[step+self._max-l: step+self._max, self._close_cl].min()

    def _resistance_break(self, levels_arr, price):
        return price>levels_arr

    def _support_break(self, levels_arr, price):
        return price<levels_arr

    def returns(self, *args):
        self.ret = {}
        for n in args:
            self.ret[n] = np.array(self.history[n:, self._close_cl])/np.array(self.history[:-n, self._close_cl])-1

    ''' Explanatory statistics '''
    def eda(self, levels=[10,30,50], axis = 0):
        self._n_levels = len(levels)
        self._max = max(levels)
        if self._training:
            self.events_hist = np.zeros([1, self._n_levels*self._n_inst])
            for step in range(self._len-self._max):
                price = self.history[step+self._max, self._close_cl]
                inst_event_hist = np.array([])
                for inst, event in zip(self._instruments, self._events):
                    event_arr = np.array([])
                    for l in levels:
                        event_arr = np.hstack((event_arr, event(inst(l, step), price)))
                    inst_event_hist = np.hstack((inst_event_hist, event_arr))
                self.events_hist = np.vstack((self.events_hist, inst_event_hist.reshape(1,-1)))
            self.events_hist = self.events_hist[1:,:]
        else:
            pass

repo_language: Python

repo_name: andreybabynin/td


text:
<file_sep>deploy.ant.properties.file=C:\\Users\\ulises\\AppData\\Roaming\\NetBeans\\8.1\\config\\GlassFishEE6\\Properties\\gfv31359507959.properties
file.reference.postgresql-9.2-1002.jdbc4.jar=C:\\Users\\ulises\\Documents\\Li<NAME>bints\\postgresql-jdbc42-9.4.1208-atlassian-hosted.jar
j2ee.platform.is.jsr109=true
j2ee.server.domain=C:/Users/ulises/AppData/Roaming/NetBeans/8.1/config/GF_4.1.1/domain1
j2ee.server.home=C:/Program Files/glassfish-4.1.1/glassfish
j2ee.server.instance=[C:\\Program Files\\glassfish-4.1.1\\glassfish;C:\\Program Files\\glassfish-4.1.1\\glassfish\\domains\\domain1]deployer:gfv3ee6wc:localhost:4848
j2ee.server.middleware=C:/Program Files/glassfish-4.1.1
selected.browser=Chrome
user.properties.file=C:\\Users\\ulises\\AppData\\Roaming\\NetBeans\\8.1\\build.properties
<file_sep>listTitle = Lista de usuarios
rolListTitle = Lista de roles
sportListTitle = Lista de deportes
moduleListTitle = Lista de modulos
athletesListTitle = Lita de Deportistas
ageDeportista = Edad
nameHeader = Nombre de usuario
nameathletHeader = Nombre del deportista
nameSportHeader = Nombre del deporte
nameNatioHeader = Nacionalidad
numJugaHeader = Numero de jugadores
apeAthletHeader = Apellido del deportista
idAthletHeader = Id deportista
idSportHeader = Id deporte
moduleNameHeader = Nombre de usuario
numdHeader = Numero de jugadores
modulePathHeader = Ruta de modulo
passwordHeader = <PASSWORD>
usertypeHeader = Tipo de usuario
namePrompt = Nombre de usuario
nameSportPrompt = Nombre del deporte
natioSportPrompt = Nacionalidad
nameAthletPrompt = Nombre del deportista
apeAthletPrompt = Apellido del deportista
ageAthletPrompt = Edad del deportista
passwordPrompt = <PASSWORD>
userTypePrompt = Tipo de usuario
rolNamePrompt = Nombre del rol
sportNamePrompt = Nombre del deporte
rolNameHeader = Rol
modulePathPrompt = Ruta de Modulo
moduleNamePrompt = Modulo
buttonSave = Guardar
buttonDelete = Eliminar
buttonCancel = Cancelar
buttonAdd = Agregar
buttonEdit = Editar
buttonSearch = Buscar
formTitle = Formulario de usuarios
formSportoTitle = Formulario de deportistas
formHeader = Gestion de usuarios
formAthletHeader = Gestion de deportistas
formSportHeader = Gestion de deportes
legendForm = Datos de usuario
legendAthletForm = Datos del deportista
legendSportForm = Datos del deporte
rolFormTitle = Formulario de Roles
sportFormTitle = Formulario de Deportes
naciFormTitle = Nacionalidad
rolFormHeader = Gesti\u00f3n de Roles
rolLegendForm = Datos del Rol
sportLegendForm = Datos del Deporte
moduleFormHeader = Gesti\u00f3n de Modulos
moduleFormTitle = Formulario de Modulos
moduleLegendForm = Datos del Modulo
systemTitle = Sistema de control de usuarios
systemWelcome = Bienvenido al sistema
developer = Desarrollador
myName = <NAME>\u00e1ndez
action = Accion
loginTitle = Acceso al Sistema
loginUser = Usuario
loginPassword = <PASSWORD>
loginButton = Acceder

repo_language: INI