amos-fernandes commited on
Commit
b3a7985
·
verified ·
1 Parent(s): 3739358

Upload 151 files

Browse files

Commit others PPO

This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. agents/AutomatedLendingPool.sol +35 -0
  2. agents/CreditOracle.sol +27 -0
  3. agents/FuzzedDataProvider.js +15 -0
  4. agents/ImeAtcoinGovernance.sol +59 -0
  5. agents/ImeAtcoinPayment.js +15 -0
  6. agents/MarketMaker.py +30 -0
  7. agents/PrivateTransactions.sol +23 -0
  8. agents/README.md +12 -0
  9. agents/RewardSystem.sol +22 -0
  10. agents/__pycache__/__init__.cpython-312.pyc +0 -0
  11. agents/__pycache__/app.cpython-312.pyc +0 -0
  12. agents/agents/DeepPortfolioAgent.py +361 -0
  13. agents/agents/__init__.py +0 -0
  14. agents/agents/__pycache__/DeepPortfolioAgent.cpython-312.pyc +0 -0
  15. agents/agents/__pycache__/DeepPortfolioAgentNetwork.cpython-312.pyc +0 -0
  16. agents/agents/__pycache__/__init__.cpython-312.pyc +0 -0
  17. agents/agents/__pycache__/config.cpython-312.pyc +0 -0
  18. agents/agents/__pycache__/custom_policies.cpython-312.pyc +0 -0
  19. agents/agents/__pycache__/data_handler_multi_asset.cpython-312.pyc +0 -0
  20. agents/agents/__pycache__/dataset_update_agent.cpython-312.pyc +0 -0
  21. agents/agents/__pycache__/deep_portfolio.cpython-312.pyc +0 -0
  22. agents/agents/__pycache__/deep_portfolio_torch.cpython-312.pyc +0 -0
  23. agents/agents/__pycache__/portfolio_environment.cpython-312.pyc +0 -0
  24. agents/agents/__pycache__/portfolio_features_extractor_torch.cpython-312.pyc +0 -0
  25. agents/agents/__pycache__/train_rl_portfolio_agent.cpython-312.pyc +0 -0
  26. agents/agents/__pycache__/train_rl_portfolio_agent_from_app.cpython-312.pyc +0 -0
  27. agents/agents/app/model/model2.h5 +3 -0
  28. agents/agents/app/model/model3.zip +3 -0
  29. agents/agents/app/model/ppo_custom_deep_portfolio_agent.zip +3 -0
  30. agents/agents/config.md +13 -0
  31. agents/agents/config.py +126 -0
  32. agents/agents/custom_policies.py +99 -0
  33. agents/agents/data_handler_multi_asset.py +448 -0
  34. agents/agents/dataset_update_agent.py +9 -0
  35. agents/agents/deep_portfolio.py +104 -0
  36. agents/agents/deep_portfolio_torch.py +80 -0
  37. agents/agents/financial_data_agent.py +76 -0
  38. agents/agents/investment_agent.py +7 -0
  39. agents/agents/portfolio_environment.py +246 -0
  40. agents/agents/portfolio_features_extractor_torch.py +35 -0
  41. agents/agents/ppo_deep_portfolio_tensorboard/PPO_1/events.out.tfevents.1750287361.verticalagent-X555LPB.89910.0 +3 -0
  42. agents/agents/ppo_deep_portfolio_tensorboard/PPO_10/events.out.tfevents.1750497081.codespaces-72cb68.2589.0 +3 -0
  43. agents/agents/ppo_deep_portfolio_tensorboard/PPO_11/events.out.tfevents.1750534135.codespaces-72cb68.3018.0 +3 -0
  44. agents/agents/ppo_deep_portfolio_tensorboard/PPO_12/events.out.tfevents.1750560310.codespaces-72cb68.253920.0 +3 -0
  45. agents/agents/ppo_deep_portfolio_tensorboard/PPO_13/events.out.tfevents.1750568153.codespaces-72cb68.2534.0 +3 -0
  46. agents/agents/ppo_deep_portfolio_tensorboard/PPO_14/events.out.tfevents.1750587177.verticalagent-X555LPB.125274.0 +3 -0
  47. agents/agents/ppo_deep_portfolio_tensorboard/PPO_15/events.out.tfevents.1750636729.verticalagent-X555LPB.266088.0 +3 -0
  48. agents/agents/ppo_deep_portfolio_tensorboard/PPO_16/events.out.tfevents.1750638335.verticalagent-X555LPB.270772.0 +3 -0
  49. agents/agents/ppo_deep_portfolio_tensorboard/PPO_17/events.out.tfevents.1750638480.verticalagent-X555LPB.271132.0 +3 -0
  50. agents/agents/ppo_deep_portfolio_tensorboard/PPO_18/events.out.tfevents.1750639418.verticalagent-X555LPB.273960.0 +3 -0
agents/AutomatedLendingPool.sol ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ pragma solidity ^0.8.0;
2
+ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
3
+ import "./CreditOracle.sol";
4
+
5
+ contract AutomatedLendingPool {
6
+ IERC20 public immutable token;
7
+ CreditOracle public immutable creditOracle;
8
+
9
+ struct Loan {
10
+ uint256 amount;
11
+ uint256 interest;
12
+ uint256 dueDate;
13
+ }
14
+
15
+ mapping(address => Loan) public loans;
16
+
17
+ constructor(address _token, address _creditOracle) {
18
+ token = IERC20(_token);
19
+ creditOracle = CreditOracle(_creditOracle);
20
+ }
21
+
22
+ function requestLoan(uint256 amount) external {
23
+ require(loans[msg.sender].amount == 0, "Existing loan must be repaid");
24
+ bytes32 requestId = creditOracle.requestCreditScore(msg.sender);
25
+ // Lógica para processar o empréstimo com base no score de crédito
26
+ }
27
+
28
+ function repayLoan() external {
29
+ Loan storage loan = loans[msg.sender];
30
+ require(loan.amount > 0, "No active loan");
31
+ uint256 totalDue = loan.amount + loan.interest;
32
+ require(token.transferFrom(msg.sender, address(this), totalDue), "Transfer failed");
33
+ delete loans[msg.sender];
34
+ }
35
+ }
agents/CreditOracle.sol ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ pragma solidity ^0.8.0;
2
+ import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
3
+
4
+ contract CreditOracle is ChainlinkClient {
5
+ using Chainlink for Chainlink.Request;
6
+
7
+ address private oracle;
8
+ bytes32 private jobId;
9
+ uint256 private fee;
10
+
11
+ constructor() {
12
+ setPublicChainlinkToken();
13
+ oracle = 0x...; // Endereço do nó Chainlink
14
+ jobId = "..."; // ID do job Chainlink
15
+ fee = 0.1 * 10 ** 18; // 0.1 LINK
16
+ }
17
+
18
+ function requestCreditScore(address user) public returns (bytes32 requestId) {
19
+ Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
20
+ request.add("userId", uint256(uint160(user)));
21
+ return sendChainlinkRequestTo(oracle, request, fee);
22
+ }
23
+
24
+ function fulfill(bytes32 _requestId, uint256 _creditScore) public recordChainlinkFulfillment(_requestId) {
25
+ // Lógica para atualizar o score de crédito do usuário
26
+ }
27
+ }
agents/FuzzedDataProvider.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const { FuzzedDataProvider } = require('fuzzing-tools');
2
+
3
+ function fuzzTest(data) {
4
+ const fuzz = new FuzzedDataProvider(data);
5
+ const amount = fuzz.consumeNumber();
6
+ const recipient = fuzz.consumeAddress();
7
+
8
+ try {
9
+ token.transfer(recipient, amount);
10
+ } catch (error) {
11
+ // Registrar e analisar erros
12
+ }
13
+ }
14
+
15
+ module.exports = { fuzzTest };
agents/ImeAtcoinGovernance.sol ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ pragma solidity ^0.8.0;
2
+ import "@openzeppelin/contracts/governance/Governor.sol";
3
+ import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
4
+ import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
5
+ import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
6
+ import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
7
+ import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
8
+
9
+ contract ImeAtcoinGovernance is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes, GovernorVotesQuorumFraction, GovernorTimelockControl {
10
+ constructor(IVotes _token, TimelockController _timelock)
11
+ Governor("ImeAtcoinGovernance")
12
+ GovernorSettings(1 /* 1 block */, 45818 /* 1 week */, 0)
13
+ GovernorVotes(_token)
14
+ GovernorVotesQuorumFraction(4)
15
+ GovernorTimelockControl(_timelock)
16
+ {}
17
+
18
+ function votingDelay() public view override(IGovernor, GovernorSettings) returns (uint256) {
19
+ return super.votingDelay();
20
+ }
21
+
22
+ function votingPeriod() public view override(IGovernor, GovernorSettings) returns (uint256) {
23
+ return super.votingPeriod();
24
+ }
25
+
26
+ function quorum(uint256 blockNumber) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) {
27
+ return super.quorum(blockNumber);
28
+ }
29
+
30
+ function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) {
31
+ return super.state(proposalId);
32
+ }
33
+
34
+ function propose(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, string memory description)
35
+ public override(Governor, IGovernor) returns (uint256)
36
+ {
37
+ return super.propose(targets, values, calldatas, description);
38
+ }
39
+
40
+ function _execute(uint256 proposalId, address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash)
41
+ internal override(Governor, GovernorTimelockControl)
42
+ {
43
+ super._execute(proposalId, targets, values, calldatas, descriptionHash);
44
+ }
45
+
46
+ function _cancel(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash)
47
+ internal override(Governor, GovernorTimelockControl) returns (uint256)
48
+ {
49
+ return super._cancel(targets, values, calldatas, descriptionHash);
50
+ }
51
+
52
+ function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) {
53
+ return super._executor();
54
+ }
55
+
56
+ function supportsInterface(bytes4 interfaceId) public view override(Governor, GovernorTimelockControl) returns (bool) {
57
+ return super.supportsInterface(interfaceId);
58
+ }
59
+ }
agents/ImeAtcoinPayment.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class ImeAtcoinPayment {
2
+ constructor(apiKey) {
3
+ this.apiKey = apiKey;
4
+ }
5
+
6
+ async createPayment(amount, currency) {
7
+ // Lógica para criar um pagamento
8
+ }
9
+
10
+ async verifyPayment(paymentId) {
11
+ // Lógica para verificar um pagamento
12
+ }
13
+ }
14
+
15
+ module.exports = ImeAtcoinPayment;
agents/MarketMaker.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ccxt
2
+
3
+ class MarketMaker:
4
+ def __init__(self, exchange, symbol, spread=0.01):
5
+ self.exchange = ccxt.exchange({'apiKey': '...', 'secret': '...'})
6
+ self.symbol = symbol
7
+ self.spread = spread
8
+
9
+ def place_orders(self):
10
+ ticker = self.exchange.fetch_ticker(self.symbol)
11
+ mid_price = (ticker['bid'] + ticker['ask']) / 2
12
+
13
+ bid_price = mid_price * (1 - self.spread / 2)
14
+ ask_price = mid_price * (1 + self.spread / 2)
15
+
16
+ self.exchange.create_limit_buy_order(self.symbol, 1, bid_price)
17
+ self.exchange.create_limit_sell_order(self.symbol, 1, ask_price)
18
+
19
+ def run(self):
20
+ while True:
21
+ try:
22
+ self.place_orders()
23
+ time.sleep(60) # Atualiza ordens a cada minuto
24
+ except Exception as e:
25
+ print(f"Error: {e}")
26
+ time.sleep(60)
27
+
28
+ if __name__ == "__main__":
29
+ market_maker = MarketMaker("binance", "I*****ME/USDT")
30
+ market_maker.run()
agents/PrivateTransactions.sol ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ pragma solidity ^0.8.0;
2
+ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
3
+ import "./ZkSnarkVerifier.sol";
4
+
5
+ contract PrivateTransactions {
6
+ IERC20 public immutable token;
7
+ ZkSnarkVerifier public immutable verifier;
8
+
9
+ constructor(address _token, address _verifier) {
10
+ token = IERC20(_token);
11
+ verifier = ZkSnarkVerifier(_verifier);
12
+ }
13
+
14
+ function privateTransfer(
15
+ uint256[2] memory a,
16
+ uint256[2][2] memory b,
17
+ uint256[2] memory c,
18
+ uint256[3] memory input
19
+ ) external {
20
+ require(verifier.verifyProof(a, b, c, input), "Invalid zk-SNARK proof");
21
+ // Executar a transferência privada
22
+ }
23
+ }
agents/README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Aibank Token
3
+ emoji: 🐠
4
+ colorFrom: yellow
5
+ colorTo: pink
6
+ sdk: static
7
+ pinned: false
8
+ license: other
9
+ short_description: Token aibank
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
agents/RewardSystem.sol ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ pragma solidity ^0.8.0;
2
+ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
3
+
4
+ contract RewardSystem {
5
+ IERC20 public immutable token;
6
+ mapping(address => uint256) public rewards;
7
+
8
+ constructor(address _token) {
9
+ token = IERC20(_token);
10
+ }
11
+
12
+ function addReward(address user, uint256 amount) external {
13
+ rewards[user] += amount;
14
+ }
15
+
16
+ function claimReward() external {
17
+ uint256 amount = rewards[msg.sender];
18
+ require(amount > 0, "No rewards to claim");
19
+ rewards[msg.sender] = 0;
20
+ require(token.transfer(msg.sender, amount), "Transfer failed");
21
+ }
22
+ }
agents/__pycache__/__init__.cpython-312.pyc CHANGED
Binary files a/agents/__pycache__/__init__.cpython-312.pyc and b/agents/__pycache__/__init__.cpython-312.pyc differ
 
agents/__pycache__/app.cpython-312.pyc ADDED
Binary file (3.16 kB). View file
 
agents/agents/DeepPortfolioAgent.py ADDED
@@ -0,0 +1,361 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # rnn/agents/deep_portfolio.py (ou onde você tem DeepPortfolioAI / DeepPortfolioAgentNetwork)
3
+ import numpy as np
4
+ from tensorflow.keras import regularizers
5
+ import tensorflow as tf
6
+ from tensorflow.keras.layers import (
7
+ Input, Conv1D, LSTM, Dense, Dropout,
8
+ MultiHeadAttention, Reshape, Concatenate,
9
+ TimeDistributed, GlobalAveragePooling1D, LayerNormalization
10
+ )
11
+ from tensorflow.keras.models import Model
12
+ from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
13
+ # Comente as importações do transformers se não for testar o sentimento agora para simplificar
14
+ # from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
15
+
16
+ # --- DEFINIÇÕES DE CONFIGURAÇÃO (COPIE OU IMPORTE DO SEU CONFIG.PY) ---
17
+ # Se você não importar do config.py, defina-as aqui para o teste
18
+ WINDOW_SIZE_CONF = 60
19
+ NUM_ASSETS_CONF = 4 # Ex: ETH, BTC, ADA, SOL
20
+ NUM_FEATURES_PER_ASSET_CONF = 26 # Número de features calculadas para CADA ativo
21
+ # (open_div_atr, ..., buy_condition_v1, etc.)
22
+ L2_REG = 0.0001 # Exemplo, use o valor do seu config
23
+
24
+ # (Cole as classes AssetProcessor e DeepPortfolioAgentNetwork aqui se estiver em um novo script)
25
+ # Ou, se estiver no mesmo arquivo, elas já estarão definidas.
26
+
27
+ # ... (Definição das classes AssetProcessor e DeepPortfolioAgentNetwork como na resposta anterior) ...
28
+ # Certifique-se que a classe DeepPortfolioAgentNetwork está usando estas constantes:
29
+ # num_assets=NUM_ASSETS_CONF,
30
+ # sequence_length=WINDOW_SIZE_CONF,
31
+ # num_features_per_asset=NUM_FEATURES_PER_ASSET_CONF
32
+
33
+ class AssetProcessor(tf.keras.Model):
34
+ def __init__(self, sequence_length, num_features, cnn_filters1=32, cnn_filters2=64, lstm_units1=64, lstm_units2=32, dropout_rate=0.2, name="single_asset_processor_module", **kwargs): # Adicionado **kwargs
35
+ super(AssetProcessor, self).__init__(name=name, **kwargs) # Adicionado **kwargs
36
+ self.sequence_length = sequence_length
37
+ self.num_features = num_features
38
+ self.cnn_filters1 = cnn_filters1 # Salvar para get_config
39
+ self.cnn_filters2 = cnn_filters2
40
+ self.lstm_units1 = lstm_units1
41
+ self.lstm_units2 = lstm_units2
42
+ self.dropout_rate = dropout_rate
43
+
44
+ self.conv1 = Conv1D(filters=cnn_filters1, kernel_size=3, activation='relu', padding='same', name="asset_cnn1")
45
+ self.dropout_cnn1 = Dropout(dropout_rate, name="asset_cnn1_dropout")
46
+ self.conv2 = Conv1D(filters=cnn_filters2, kernel_size=3, activation='relu', padding='same', name="asset_cnn2")
47
+ self.dropout_cnn2 = Dropout(dropout_rate, name="asset_cnn2_dropout")
48
+ self.lstm1 = LSTM(lstm_units1, return_sequences=True, name="asset_lstm1")
49
+ self.dropout_lstm1 = Dropout(dropout_rate, name="asset_lstm1_dropout")
50
+ self.lstm2 = LSTM(lstm_units2, return_sequences=False, name="asset_lstm2_final")
51
+ self.dropout_lstm2 = Dropout(dropout_rate, name="asset_lstm2_dropout")
52
+
53
+ def call(self, inputs, training=False):
54
+ x = self.conv1(inputs)
55
+ x = self.dropout_cnn1(x, training=training)
56
+ x = self.conv2(x)
57
+ x = self.dropout_cnn2(x, training=training)
58
+ x = self.lstm1(x, training=training)
59
+ x = self.dropout_lstm1(x, training=training)
60
+ x = self.lstm2(x, training=training)
61
+ x_processed_asset = self.dropout_lstm2(x, training=training)
62
+ return x_processed_asset
63
+
64
+ def get_config(self):
65
+ config = super().get_config()
66
+ config.update({
67
+ "sequence_length": self.sequence_length,
68
+ "num_features": self.num_features,
69
+ "cnn_filters1": self.cnn_filters1,
70
+ "cnn_filters2": self.cnn_filters2,
71
+ "lstm_units1": self.lstm_units1,
72
+ "lstm_units2": self.lstm_units2,
73
+ "dropout_rate": self.dropout_rate,
74
+ })
75
+ return config
76
+
77
+ class DeepPortfolioAgentNetwork(tf.keras.Model):
78
+ def __init__(self,
79
+ num_assets=int(NUM_ASSETS_CONF),
80
+ sequence_length=int(WINDOW_SIZE_CONF),
81
+ num_features_per_asset=int(NUM_FEATURES_PER_ASSET_CONF),
82
+ asset_cnn_filters1=32, asset_cnn_filters2=64,
83
+ asset_lstm_units1=64, asset_lstm_units2=32, asset_dropout=0.2,
84
+ mha_num_heads=4, mha_key_dim_divisor=2, # key_dim será asset_lstm_units2 // mha_key_dim_divisor
85
+ final_dense_units1=128, final_dense_units2=64, final_dropout=0.3,
86
+ use_sentiment_analysis=True,
87
+ output_latent_features=False, **kwargs): # Adicionado **kwargs
88
+ super(DeepPortfolioAgentNetwork, self).__init__(name="deep_portfolio_agent_network", **kwargs) # Adicionado **kwargs
89
+
90
+
91
+ print(f"DPN __init__ > num_assets ENTRADA: {num_assets}, tipo: {type(num_assets)}")
92
+
93
+ # Tentar extrair o valor escalar se for um tensor/variável ou TrackedDict
94
+ def get_int_value(param_name, val):
95
+ if isinstance(val, (tf.Tensor, tf.Variable)):
96
+ if val.shape == tf.TensorShape([]): # Escalar
97
+ print(f"DPN __init__: Convertendo {param_name} (Tensor/Variable escalar) para int.")
98
+ return int(val.numpy())
99
+ else:
100
+ raise ValueError(f"{param_name} é um Tensor/Variable mas não é escalar. Shape: {val.shape}")
101
+ elif isinstance(val, dict): # Pode ser um TrackedDict
102
+ # TrackedDict pode se comportar como um dict. Se o valor real está "escondido",
103
+ # precisamos descobrir como acessá-lo.
104
+ # Por agora, vamos tentar a conversão direta, e se falhar, o erro será mais claro.
105
+ # Se for um dict simples com uma chave específica, você precisaria dessa chave.
106
+ # O erro 'KeyError: value' sugere que ['value'] não é a forma correta.
107
+ # Geralmente, para hiperparâmetros, o TrackedDict deve conter o valor diretamente
108
+ # se o SB3 o passou corretamente.
109
+ print(f"DPN __init__: Tentando converter {param_name} (dict-like) para int.")
110
+ try:
111
+ return int(val) # Tentar conversão direta
112
+ except TypeError:
113
+ # Se TrackedDict se comporta como um tensor quando usado em ops TF,
114
+ # tf.get_static_value pode funcionar, ou apenas o uso direto
115
+ # em operações TF (mas range() não é uma op TF).
116
+ # Se for um tensor TF "disfarçado", .numpy() pode funcionar.
117
+ # Se for um dict com uma chave específica, essa chave seria necessária.
118
+ # O erro mostra que ['value'] não funcionou.
119
+ print(f"DPN __init__: Conversão direta de {param_name} (dict-like) para int falhou. Investigar TrackedDict.")
120
+ # Para depuração, você pode tentar imprimir os itens do dict:
121
+ # if isinstance(val, collections.abc.Mapping): # Checa se é um dict-like
122
+ # for k, v_item in val.items():
123
+ # print(f" {param_name} item: {k} -> {v_item}")
124
+ raise TypeError(f"{param_name} é {type(val)} e não pôde ser convertido para int diretamente. Valor: {val}")
125
+ else: # Tenta conversão direta para outros tipos
126
+ return int(val)
127
+
128
+ try:
129
+ self.num_assets = get_int_value("num_assets", num_assets)
130
+ self.sequence_length = get_int_value("sequence_length", sequence_length)
131
+ self.num_features_per_asset = get_int_value("num_features_per_asset", num_features_per_asset)
132
+ self.asset_lstm_output_dim = get_int_value("asset_lstm_units2", asset_lstm_units2) # Do kwargs
133
+
134
+ # Faça o mesmo para TODOS os outros parâmetros que devem ser inteiros e são passados
135
+ # para construtores de camadas Keras (cnn_filters, lstm_units, mha_num_heads, etc.)
136
+ # Exemplo:
137
+ # self.asset_cnn_filters1_val = get_int_value("asset_cnn_filters1", kwargs.get("asset_cnn_filters1"))
138
+
139
+ except Exception as e_conv:
140
+ print(f"ERRO CRÍTICO DE CONVERSÃO DE TIPO no __init__ da DeepPortfolioAgentNetwork: {e_conv}")
141
+ raise
142
+
143
+ print(f"DPN __init__ > self.num_assets APÓS conversão: {self.num_assets}, tipo: {type(self.num_assets)}")
144
+
145
+
146
+
147
+
148
+
149
+
150
+ self.num_assets = num_assets
151
+ self.sequence_length = sequence_length
152
+ self.num_features_per_asset = num_features_per_asset
153
+ self.asset_lstm_output_dim = asset_lstm_units2
154
+
155
+ self.asset_processor = AssetProcessor(
156
+ sequence_length=self.sequence_length, num_features=self.num_features_per_asset,
157
+ cnn_filters1=asset_cnn_filters1, cnn_filters2=asset_cnn_filters2,
158
+ lstm_units1=asset_lstm_units1, lstm_units2=asset_lstm_units2,
159
+ dropout_rate=asset_dropout
160
+ )
161
+
162
+ # Ajustar key_dim para ser compatível com a dimensão de entrada e num_heads
163
+ # key_dim * num_heads deve ser idealmente igual a asset_lstm_output_dim se for auto-atenção direta,
164
+ # ou o MHA projeta internamente. Para simplificar, vamos fazer key_dim ser divisível.
165
+ # Se asset_lstm_output_dim não for divisível por num_heads, key_dim pode ser diferente.
166
+ # Vamos definir key_dim explicitamente. Se asset_lstm_output_dim = 32 e num_heads = 4, key_dim pode ser 8.
167
+ # Ou deixar o MHA lidar com a projeção se key_dim for diferente.
168
+ # Para maior clareza, calculamos uma key_dim sensata.
169
+ calculated_key_dim = self.asset_lstm_output_dim // mha_key_dim_divisor
170
+ if calculated_key_dim == 0: # Evitar key_dim zero
171
+ calculated_key_dim = self.asset_lstm_output_dim # Fallback se for muito pequeno
172
+ print(f"AVISO: asset_lstm_output_dim ({self.asset_lstm_output_dim}) muito pequeno para mha_key_dim_divisor ({mha_key_dim_divisor}). Usando key_dim = {calculated_key_dim}")
173
+
174
+ self.attention = MultiHeadAttention(num_heads=mha_num_heads, key_dim=calculated_key_dim, dropout=0.1, name="multi_asset_attention")
175
+ self.attention_norm = LayerNormalization(epsilon=1e-6, name="attention_layernorm")
176
+ self.global_avg_pool_attention = GlobalAveragePooling1D(name="gap_after_attention")
177
+
178
+ self.use_sentiment = use_sentiment_analysis # Desabilitado por padrão para este teste
179
+ self.sentiment_embedding_size = 3
180
+ if self.use_sentiment:
181
+ try:
182
+ self.tokenizer = AutoTokenizer.from_pretrained('ProsusAI/finbert')
183
+ self.sentiment_model = TFAutoModelForSequenceClassification.from_pretrained('ProsusAI/finbert', from_pt=True)
184
+ print("Modelo FinBERT carregado para análise de sentimento.")
185
+ except Exception as e:
186
+ print(f"AVISO: Falha ao carregar FinBERT: {e}. Análise de sentimento será desabilitada.")
187
+ self.use_sentiment = False
188
+
189
+
190
+ dense_input_dim = self.use_sentiment
191
+ #if self.use_sentiment: dense_input_dim += self.sentiment_embedding_size
192
+
193
+ self.dense1 = Dense(final_dense_units1, activation='relu', kernel_regularizer=regularizers.l2(L2_REG), name="final_dense1")
194
+ self.dropout1 = Dropout(final_dropout, name="final_dropout1")
195
+ self.dense2 = Dense(final_dense_units2, activation='relu', kernel_regularizer=regularizers.l2(L2_REG), name="final_dense2")
196
+ self.dropout2 = Dropout(final_dropout, name="final_dropout2")
197
+ self.output_allocation = Dense(self.num_assets, activation='softmax', name="portfolio_allocation_output")
198
+
199
+ def call(self, inputs, training=False):
200
+ market_data_flat = inputs
201
+
202
+ print(type(self.num_assets))
203
+ asset_representations_list = []
204
+ for i in range(self.num_assets):
205
+ start_idx = i * self.num_features_per_asset
206
+ end_idx = (i + 1) * self.num_features_per_asset
207
+ current_asset_data = market_data_flat[:, :, start_idx:end_idx]
208
+ processed_asset_representation = self.asset_processor(current_asset_data, training=training)
209
+ asset_representations_list.append(processed_asset_representation)
210
+
211
+ stacked_asset_features = tf.stack(asset_representations_list, axis=1)
212
+
213
+ # Para MHA, query, value, key são (batch_size, Tq, dim), (batch_size, Tv, dim)
214
+ # Aqui, T = num_assets, dim = asset_lstm_output_dim
215
+ attention_output = self.attention(
216
+ query=stacked_asset_features, value=stacked_asset_features, key=stacked_asset_features,
217
+ training=training
218
+ )
219
+ attention_output = self.attention_norm(stacked_asset_features + attention_output)
220
+
221
+ context_vector_from_attention = self.global_avg_pool_attention(attention_output)
222
+
223
+ current_features_for_dense = context_vector_from_attention
224
+ # if self.use_sentiment: ... (lógica de concatenação)
225
+
226
+ x = self.dense1(current_features_for_dense)
227
+ x = self.dropout1(x, training=training)
228
+ x = self.dense2(x)
229
+ x = self.dropout2(x, training=training)
230
+
231
+ portfolio_weights = self.output_allocation(x)
232
+ return portfolio_weights
233
+
234
+ def get_config(self): # Necessário se você quiser salvar/carregar o modelo que usa este sub-modelo
235
+ config = super().get_config()
236
+ config.update({
237
+ "num_assets": self.num_assets,
238
+ "sequence_length": self.sequence_length,
239
+ "num_features_per_asset": self.num_features_per_asset,
240
+ # Adicione outros args do __init__ aqui para todas as camadas e sub-modelos
241
+ "asset_lstm_output_dim": self.asset_lstm_output_dim,
242
+ # ... e os parâmetros passados para AssetProcessor e MHA, etc.
243
+ })
244
+ return config
245
+
246
+ # @classmethod
247
+ # def from_config(cls, config): # Necessário para carregar com sub-modelo customizado
248
+ # # Extrair config do AssetProcessor se necessário
249
+ # return cls(**config)
250
+
251
+
252
+ if __name__ == '__main__':
253
+ print("Testando o Forward Pass do DeepPortfolioAgentNetwork...")
254
+
255
+ # 1. Definir Parâmetros para o Teste (devem corresponder ao config.py)
256
+ batch_size_test = 2 # Um batch pequeno para teste
257
+ seq_len_test = WINDOW_SIZE_CONF
258
+ num_assets_test = NUM_ASSETS_CONF
259
+ num_features_per_asset_test = NUM_FEATURES_PER_ASSET_CONF
260
+ total_features_flat = num_assets_test * num_features_per_asset_test
261
+
262
+ print(f"Configuração do Teste:")
263
+ print(f" Batch Size: {batch_size_test}")
264
+ print(f" Sequence Length (Window): {seq_len_test}")
265
+ print(f" Number of Assets: {num_assets_test}")
266
+ print(f" Features per Asset: {num_features_per_asset_test}")
267
+ print(f" Total Flat Features per Timestep: {total_features_flat}")
268
+
269
+ # 2. Criar Tensor de Input Mockado
270
+ # Shape: (batch_size, sequence_length, num_assets * num_features_per_asset)
271
+ mock_market_data_flat = tf.random.normal(
272
+ shape=(batch_size_test, seq_len_test, total_features_flat)
273
+ )
274
+ print(f"Shape do Input Mockado (market_data_flat): {mock_market_data_flat.shape}")
275
+
276
+ # 3. Instanciar o Modelo
277
+ # Use os mesmos hiperparâmetros que você definiria no config.py para a rede
278
+ print("\nInstanciando DeepPortfolioAgentNetwork...")
279
+ agent_network = DeepPortfolioAgentNetwork(
280
+ num_assets=num_assets_test,
281
+ sequence_length=seq_len_test,
282
+ num_features_per_asset=num_features_per_asset_test,
283
+ # Você pode variar os próximos parâmetros para testar diferentes configs
284
+ asset_cnn_filters1=32, asset_cnn_filters2=64,
285
+ asset_lstm_units1=64, asset_lstm_units2=32, # asset_lstm_units2 define asset_lstm_output_dim
286
+ asset_dropout=0.1,
287
+ mha_num_heads=4, mha_key_dim_divisor=4, # Ex: 32 // 4 = 8 para key_dim
288
+ final_dense_units1=64, final_dense_units2=32, final_dropout=0.2,
289
+ use_sentiment_analysis=False # Testar sem sentimento primeiro
290
+ )
291
+
292
+ # Para construir o modelo e ver o summary, você pode chamar com o input mockado
293
+ # ou explicitamente chamar model.build() se souber o input shape completo
294
+ # Chamar com input mockado é mais fácil para construir.
295
+ print("\nConstruindo o modelo com input mockado (primeira chamada)...")
296
+ try:
297
+ # É uma boa prática fazer a primeira chamada dentro de um tf.function para otimizar
298
+ # ou apenas chamar diretamente para teste.
299
+ _ = agent_network(mock_market_data_flat) # Chamada para construir as camadas
300
+ print("\n--- Summary da Rede Principal (DeepPortfolioAgentNetwork) ---")
301
+ agent_network.summary()
302
+
303
+ # O summary do asset_processor já foi impresso no __init__ do DeepPortfolioAgentNetwork
304
+ # se você descomentar as linhas de build/summary lá.
305
+ # Ou você pode imprimir aqui:
306
+ print("\n--- Summary do AssetProcessor (Sub-Modelo) ---")
307
+ agent_network.asset_processor.summary()
308
+
309
+
310
+ except Exception as e:
311
+ print(f"Erro ao construir a rede principal: {e}", exc_info=True)
312
+ exit()
313
+
314
+ # 4. Chamar model(mock_input) para o Forward Pass
315
+ print("\nExecutando Forward Pass...")
316
+ try:
317
+ predictions = agent_network(mock_market_data_flat, training=False) # Passar training=False para inferência
318
+ print("Forward Pass concluído com sucesso!")
319
+ except Exception as e:
320
+ print(f"Erro durante o Forward Pass: {e}", exc_info=True)
321
+ exit()
322
+
323
+ # 5. Verificar o Shape da Saída
324
+ print(f"\nShape da Saída (predictions): {predictions.shape}")
325
+ expected_output_shape = (batch_size_test, num_assets_test)
326
+ if predictions.shape == expected_output_shape:
327
+ print(f"Shape da Saída está CORRETO! Esperado: {expected_output_shape}")
328
+ else:
329
+ print(f"ERRO: Shape da Saída INCORRETO. Esperado: {expected_output_shape}, Obtido: {predictions.shape}")
330
+
331
+ # Verificar se a saída é uma distribuição de probabilidade (softmax)
332
+ if hasattr(predictions, 'numpy'): # Se for um EagerTensor
333
+ output_sum = tf.reduce_sum(predictions, axis=-1).numpy()
334
+ print(f"Soma das probabilidades de saída por amostra no batch (deve ser próximo de 1): {output_sum}")
335
+ if np.allclose(output_sum, 1.0):
336
+ print("Saída Softmax parece CORRETA (soma 1).")
337
+ else:
338
+ print("AVISO: Saída Softmax pode NÃO estar correta (soma diferente de 1).")
339
+
340
+ print("\nExemplo das primeiras predições (pesos do portfólio):")
341
+ print(predictions.numpy()[:min(5, batch_size_test)]) # Imprime até 5 predições do batch
342
+
343
+ # Teste com sentimento (se implementado e FinBERT carregado)
344
+ # agent_network.use_sentiment = True # Ativar para teste
345
+ # if agent_network.use_sentiment and hasattr(agent_network, 'tokenizer'):
346
+ # print("\nTestando Forward Pass COM SENTIMENTO...")
347
+ # mock_news_batch = ["positive news for asset 1", "market is very volatile today"] # Exemplo
348
+ # # A forma como você passa 'news' para o call() precisa ser definida.
349
+ # # Se for um dicionário:
350
+ # # mock_inputs_with_news = {"market_data": mock_market_data_flat, "news_data": mock_news_batch}
351
+ # # predictions_with_sentiment = agent_network(mock_inputs_with_news, training=False)
352
+ # # print(f"Shape da Saída com Sentimento: {predictions_with_sentiment.shape}")
353
+ # else:
354
+ # print("\nTeste com sentimento pulado (use_sentiment=False ou FinBERT não carregado).")
355
+
356
+ print("\nTeste do Forward Pass Concluído!")
357
+
358
+
359
+
360
+
361
+
agents/agents/__init__.py ADDED
File without changes
agents/agents/__pycache__/DeepPortfolioAgent.cpython-312.pyc ADDED
Binary file (14.7 kB). View file
 
agents/agents/__pycache__/DeepPortfolioAgentNetwork.cpython-312.pyc ADDED
Binary file (6.39 kB). View file
 
agents/agents/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (156 Bytes). View file
 
agents/agents/__pycache__/config.cpython-312.pyc ADDED
Binary file (2.71 kB). View file
 
agents/agents/__pycache__/custom_policies.cpython-312.pyc ADDED
Binary file (5.13 kB). View file
 
agents/agents/__pycache__/data_handler_multi_asset.cpython-312.pyc ADDED
Binary file (14.4 kB). View file
 
agents/agents/__pycache__/dataset_update_agent.cpython-312.pyc ADDED
Binary file (850 Bytes). View file
 
agents/agents/__pycache__/deep_portfolio.cpython-312.pyc ADDED
Binary file (5.93 kB). View file
 
agents/agents/__pycache__/deep_portfolio_torch.cpython-312.pyc ADDED
Binary file (4.86 kB). View file
 
agents/agents/__pycache__/portfolio_environment.cpython-312.pyc ADDED
Binary file (10.7 kB). View file
 
agents/agents/__pycache__/portfolio_features_extractor_torch.cpython-312.pyc ADDED
Binary file (1.68 kB). View file
 
agents/agents/__pycache__/train_rl_portfolio_agent.cpython-312.pyc ADDED
Binary file (16.2 kB). View file
 
agents/agents/__pycache__/train_rl_portfolio_agent_from_app.cpython-312.pyc ADDED
Binary file (16.2 kB). View file
 
agents/agents/app/model/model2.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a6e092222aca7f924ce78038117a42b2a69ad5dfd3727d0999e8e880e8a1648f
3
+ size 1178756
agents/agents/app/model/model3.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a6e092222aca7f924ce78038117a42b2a69ad5dfd3727d0999e8e880e8a1648f
3
+ size 1178756
agents/agents/app/model/ppo_custom_deep_portfolio_agent.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a6e092222aca7f924ce78038117a42b2a69ad5dfd3727d0999e8e880e8a1648f
3
+ size 1178756
agents/agents/config.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # EXPECTED_FEATURES_ORDER define como as colunas DEVEM ESTAR no DataFrame
2
+ # que entra na função create_sequences, APÓS o escalonamento e ANTES do sufixo _scaled.
3
+ # E também como o rnn_predictor.py espera as features ANTES de aplicar os scalers.
4
+ # Se rnn_predictor.py vai aplicar scalers separados, ele precisa dos nomes originais (ou _div_atr).
5
+ # O script de treino vai criar colunas com sufixo _scaled para alimentar o modelo.
6
+ # A lista EXPECTED_FEATURES_ORDER no config.py não é diretamente usada no train_rnn_model.py
7
+ # da forma como está agora, mas é CRUCIAL para alinhar com rnn_predictor.py.
8
+ # Fazer o train_rnn_model.py funcionar e depois alinhar o rnn_predictor.py.
9
+ # Importante que as NUM_FEATURES e o input_shape do modelo estejam corretos.
10
+
11
+ # Esta variável será usada para nomear as colunas escaladas que vão para o modelo
12
+ # e deve corresponder ao que o rnn_predictor.py espera encontrar como features escaladas.
13
+ # Os nomes aqui devem ser as colunas de BASE_FEATURE_COLS com "_scaled" no final.
agents/agents/config.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # config.py
2
+
3
+ # --- Parâmetros de Dados ---
4
+ SYMBOL = 'ETH/USDT' # Ativo principal para o modelo de classificação (se ainda usar)
5
+ MULTI_ASSET_SYMBOLS = { # Para o agente de portfólio RL
6
+ 'eth': 'ETH-USD', # Chave amigável: ticker_yfinance
7
+ 'btc': 'BTC-USD',
8
+ 'ada': 'ADA-USD',
9
+ 'sol': 'SOL-USD'
10
+ }
11
+ NUM_ASSETS_PORTFOLIO = len(MULTI_ASSET_SYMBOLS) # Número de ativos no portfólio
12
+ NUM_ASSETS=4
13
+ TIMEFRAME = '1h' # Usado tanto para yfinance quanto para ccxt (se adaptar)
14
+ DAYS_OF_DATA_TO_FETCH = 365 * 2
15
+ LIMIT_PER_FETCH = 1000 # Para ccxt
16
+
17
+ # --- Parâmetros de Features e Janela ---
18
+ WINDOW_SIZE = 60
19
+
20
+ # BASE_FEATURE_COLS: Colunas calculadas para CADA ativo ANTES do escalonamento final para o modelo.
21
+ # Estas são as features que seu data_handler_multi_asset.py DEVE produzir para cada ativo.
22
+ # O rnn_predictor.py (ou a parte de features da DeepPortfolioAgentNetwork) também as calculará.
23
+ BASE_FEATURES_PER_ASSET_INPUT = [ # Renomeado para clareza
24
+ 'open_div_atr', 'high_div_atr', 'low_div_atr', 'close_div_atr', 'volume_div_atr',
25
+ 'log_return', 'rsi_14', 'atr', 'bbp', 'cci_37', 'mfi_37',
26
+ 'body_size_norm_atr', 'body_vs_avg_body', 'macd', 'sma_10_div_atr',
27
+ 'adx_14', 'volume_zscore', 'buy_condition_v1'
28
+ # Se 'cond_compra_v1' for diferente de 'buy_condition_v1', adicione aqui.
29
+ # Se for igual, remova a redundância. Assumindo que 'buy_condition_v1' é a correta.
30
+ ]
31
+ # Nomes das colunas de preço/volume (normalizadas por ATR) que usarão o price_vol_scaler
32
+ API_PRICE_VOL_COLS = ['open_div_atr', 'high_div_atr', 'low_div_atr', 'close_div_atr', 'volume_div_atr', 'body_size_norm_atr']
33
+ # Nomes das colunas de indicadores (e outras) que usarão o indicator_scaler
34
+ API_INDICATOR_COLS = [col for col in BASE_FEATURES_PER_ASSET_INPUT if col not in API_PRICE_VOL_COLS]
35
+
36
+ # Número de features que CADA ativo terá após todos os cálculos e ANTES do escalonamento final.
37
+ NUM_FEATURES_PER_ASSET = len(BASE_FEATURES_PER_ASSET_INPUT)
38
+
39
+ # Nomes das colunas escaladas que o modelo RNN/RL efetivamente verá como entrada.
40
+ # Esta é a ordem que deve ser mantida após o escalonamento no data_handler e no rnn_predictor.
41
+ # E também o que o create_sequences espera.
42
+ EXPECTED_SCALED_FEATURES_FOR_MODEL = [f"{col}_scaled" for col in BASE_FEATURES_PER_ASSET_INPUT]
43
+ # NUM_FEATURES_MODEL_INPUT será len(EXPECTED_SCALED_FEATURES_FOR_MODEL), que é igual a NUM_FEATURES_PER_ASSET
44
+
45
+ # --- Parâmetros do Alvo da Predição (Para o Modelo de Classificação Supervisionado, se ainda usar) ---
46
+ PREDICTION_HORIZON = 5
47
+ PRICE_CHANGE_THRESHOLD = 0.0075
48
+
49
+ # --- Parâmetros da Rede Neural (DeepPortfolioAgentNetwork e seu AssetProcessor) ---
50
+ # Para AssetProcessor (processamento individual de ativo)
51
+ ASSET_CNN_FILTERS1 = 32
52
+ ASSET_CNN_FILTERS2 = 64
53
+ ASSET_LSTM_UNITS1 = 64
54
+ ASSET_LSTM_UNITS2 = 32 # Saída do AssetProcessor, se torna a dimensão da feature latente por ativo
55
+ ASSET_DROPOUT = 0.2
56
+
57
+ # Para DeepPortfolioAgentNetwork (camadas após processamento individual)
58
+ MHA_NUM_HEADS = 4
59
+ # key_dim da MHA será ASSET_LSTM_UNITS2 // MHA_KEY_DIM_DIVISOR
60
+ MHA_KEY_DIM_DIVISOR = 2 # Ex: 32 // 2 = 16. Garanta que ASSET_LSTM_UNITS2 seja divisível. Se não, ajuste.
61
+
62
+ # Camadas densas FINAIS DENTRO da DeepPortfolioAgentNetwork, ANTES da saída de features latentes
63
+ # ou da camada de alocação softmax (se não estiver retornando features latentes).
64
+ # `FINAL_DENSE_UNITS2_EXTRACTOR` será a dimensão das features que o extrator cospe para o SB3.
65
+ DPN_FINAL_DENSE1_UNITS = 64 # "DPN" para DeepPortfolioNetwork
66
+ DPN_LATENT_FEATURE_DIM = 32 # Saída da DPN quando output_latent_features=True. IGUAL A ASSET_LSTM_UNITS2 se não houver mais camadas após GAP.
67
+ # Se você adicionou Dense(final_dense_units1) e Dense(final_dense_units2) APÓS a atenção
68
+ # no DeepPortfolioAgentNetwork, então DPN_LATENT_FEATURE_DIM seria final_dense_units2.
69
+ # No nosso último design, era a saída do global_avg_pool_attention, então ASSET_LSTM_UNITS2.
70
+ # Vamos assumir que a saída do GAP é usada como feature latente por enquanto.
71
+ # DPN_LATENT_FEATURE_DIM = ASSET_LSTM_UNITS2
72
+
73
+ # Ajustando com base no seu código de `deep_portfolio.py` onde você tinha `final_dense_units1` e `final_dense_units2`
74
+ # após a atenção e antes do output de alocação.
75
+ # Estas são as camadas que produzem as features latentes para SB3.
76
+ DPN_SHARED_HEAD_DENSE1_UNITS = 128 # Corresponde a final_dense_units1 na sua DeepPortfolioAgentNetwork
77
+ DPN_SHARED_HEAD_LATENT_DIM = 64 # Corresponde a final_dense_units2, que será o self.features_dim do extrator
78
+ DPN_SHARED_HEAD_DROPOUT = 0.3
79
+
80
+ DEFAULT_EXTRACTOR_KWARGS=DPN_SHARED_HEAD_DENSE1_UNITS
81
+
82
+ # Para as cabeças de Política (Ator) e Valor (Crítico) no Stable-Baselines3 (APÓS o extrator)
83
+ # Se vazias, a saída do extrator é usada diretamente para as camadas finais de ação/valor.
84
+ POLICY_HEAD_NET_ARCH = [64] # Ex: [64, 32] ou [] se não quiser camadas extras
85
+ VALUE_HEAD_NET_ARCH = [64] # Ex: [64, 32] ou []
86
+
87
+ # --- Parâmetros Gerais do Modelo (se aplicável a ambos os tipos de modelo) ---
88
+ MODEL_DROPOUT_RATE = 0.3 # Você usou 0.3 na última rodada de classificação bem-sucedida
89
+ MODEL_L2_REG = 0.0001 # Você usou 0.0001 ou 0.0005
90
+
91
+ L2_REG = 0.0001
92
+
93
+ # --- Parâmetros de Treinamento ---
94
+ # Para o modelo de classificação supervisionado (se ainda usar)
95
+ SUPERVISED_LEARNING_RATE = 0.0005
96
+ SUPERVISED_BATCH_SIZE = 128
97
+ SUPERVISED_EPOCHS = 100
98
+ LEARNING_RATE=0.0005
99
+
100
+ # Para o agente RL (PPO)
101
+ PPO_LEARNING_RATE = 0.0003 # Padrão do SB3 PPO, pode ajustar
102
+ PPO_N_STEPS = 2048
103
+ PPO_BATCH_SIZE_RL = 64 # Mini-batch size do PPO
104
+ PPO_ENT_COEF = 0.0
105
+ PPO_TOTAL_TIMESTEPS = 2048 #1000000 # Comece com menos para teste (ex: 50k-100k)
106
+
107
+ # --- Parâmetros do Ambiente RL ---
108
+ # RISK_FREE_RATE_ANNUAL = 0.02 # Taxa livre de risco anual (ex: 2%)
109
+ # REWARD_WINDOW_SHARPE = 252 * 1 # Ex: Janela de 1 ano de dados horários para Sharpe (252 dias * 24h)
110
+ # Ou uma janela menor como 60 ou 120 passos.
111
+ INITIAL_BALANCE = 100000
112
+ TRANSACTION_COST_PCT = 0.001 # 0.1%
113
+
114
+ # --- Caminhos para Salvar ---
115
+ MODEL_ROOT_DIR = "app/model" # Diretório raiz para todos os modelos e scalers
116
+ # Para modelo de classificação supervisionado (se mantiver)
117
+ SUPERVISED_MODEL_NAME = "classification_model.h5"
118
+ SUPERVISED_PV_SCALER_NAME = "supervisor_pv_scaler.joblib"
119
+ SUPERVISED_IND_SCALER_NAME = "supervisor_ind_scaler.joblib"
120
+ # Para modelo RL (agente PPO salvo pelo SB3)
121
+ RL_AGENT_MODEL_NAME = "ppo_deep_portfolio_agent" # SB3 adiciona .zip
122
+ # Scalers usados para preparar dados para o DeepPortfolioAgentNetwork (que é o extrator do RL)
123
+ RL_PV_SCALER_NAME = "rl_price_volume_atr_norm_scaler.joblib" # Seus nomes descritivos
124
+ RL_INDICATOR_SCALER_NAME = "rl_other_indicators_scaler.joblib"
125
+ FINAL_DENSE_UNITS1_EXTRACTOR=DEFAULT_EXTRACTOR_KWARGS
126
+ USE_SENTIMENT_CONFIG=True
agents/agents/custom_policies.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # rnn/agents/custom_policies.py (NOVO ARQUIVO, ou adicione ao deep_portfolio.py)
2
+
3
+ import gymnasium as gym # Usar gymnasium
4
+ import tensorflow as tf
5
+ from stable_baselines3.common.torch_layers import BaseFeaturesExtractor as PyTorchBaseFeaturesExtractor
6
+
7
+ from stable_baselines3.common.torch_layers import MlpExtractor
8
+ import torch.nn as nn
9
+ import torch
10
+
11
+ class CustomTFMlpExtractor(tf.keras.layers.Layer):
12
+ def __init__(self, feature_dim, net_arch, activation_fn=tf.nn.relu):
13
+ super().__init__()
14
+ self.net = tf.keras.Sequential()
15
+ for units in net_arch:
16
+ self.net.add(tf.keras.layers.Dense(units))
17
+ self.net.add(tf.keras.layers.Activation(activation_fn))
18
+
19
+ def call(self, inputs, training=False):
20
+ return self.net(inputs, training=training)
21
+
22
+
23
+
24
+ class CustomMlpExtractor(MlpExtractor):
25
+ def __init__(self, input_dim, net_arch, activation_fn, device):
26
+ super().__init__(input_dim, net_arch, activation_fn, device)
27
+
28
+ def forward(self, features):
29
+ for layer in self.policy_net:
30
+ if isinstance(layer, nn.ReLU):
31
+ features = layer(features) # Passando 'features' como argumento
32
+ else:
33
+ features = layer(features)
34
+ return features
35
+ # Para TensorFlow, precisamos de um extrator de features compatível ou construir a política de forma diferente.
36
+ # Stable Baselines3 tem melhor suporte nativo para PyTorch. Para TF, é um pouco mais manual.
37
+ # VAMOS USAR A ABORDAGEM DE POLÍTICA CUSTOMIZADA COM TF DIRETAMENTE.
38
+ from stable_baselines3.common.policies import ActorCriticPolicy
39
+ from typing import List, Dict, Any, Optional, Union, Type
40
+ # Importar sua rede e configs
41
+ #import agents.DeepPortfolioAgent as DeepPortfolioAgent
42
+ from portfolio_features_extractor_torch import PortfolioFeaturesExtractorTorch
43
+ # from ..config import (NUM_ASSETS, WINDOW_SIZE, NUM_FEATURES_PER_ASSET, ...) # Importe do seu config real
44
+ # VALORES DE EXEMPLO (PEGUE DO SEU CONFIG.PY REAL)
45
+ NUM_ASSETS_POLICY = 4
46
+ WINDOW_SIZE_POLICY = 60
47
+ NUM_FEATURES_PER_ASSET_POLICY = 26
48
+ # Hiperparâmetros para DeepPortfolioAgentNetwork quando usada como extrator
49
+ ASSET_CNN_FILTERS1_POLICY = 32
50
+ ASSET_CNN_FILTERS2_POLICY = 64
51
+ ASSET_LSTM_UNITS1_POLICY = 64
52
+ ASSET_LSTM_UNITS2_POLICY = 32 # Esta será a dimensão das features latentes para ator/crítico
53
+ ASSET_DROPOUT_POLICY = 0.2
54
+ MHA_NUM_HEADS_POLICY = 4
55
+ MHA_KEY_DIM_DIVISOR_POLICY = 2 # Para key_dim = 32 // 2 = 16
56
+ FINAL_DENSE_UNITS1_POLICY = 128
57
+ FINAL_DENSE_UNITS2_POLICY = ASSET_LSTM_UNITS2_POLICY # A saída da dense2 SÃO as features latentes
58
+ FINAL_DROPOUT_POLICY = 0.3
59
+
60
+
61
+
62
+
63
+ class CustomPolicy(ActorCriticPolicy):
64
+ def __init__(self, *args, **kwargs):
65
+ super().__init__(*args, **kwargs)
66
+ self.mlp_extractor = CustomTFMlpExtractor(
67
+ input_dim=self.observation_space.shape[0],
68
+ net_arch=[64, 64], # Exemplo de arquitetura
69
+ activation_fn=nn.ReLU,
70
+ device=self.device
71
+ )
72
+
73
+ from torch import nn
74
+
75
+ class CustomPortfolioPolicySB3(ActorCriticPolicy):
76
+ def __init__(
77
+ self,
78
+ observation_space: gym.spaces.Space,
79
+ action_space: gym.spaces.Space,
80
+ lr_schedule,
81
+ net_arch: Optional[List[Union[int, Dict[str, List[int]]]]] = None,
82
+ activation_fn: Type[nn.Module] = nn.ReLU, # Use PyTorch ReLU
83
+ features_extractor_kwargs: Optional[Dict[str, Any]] = None,
84
+ **kwargs,
85
+ ):
86
+ if features_extractor_kwargs is None:
87
+ features_extractor_kwargs = {}
88
+ features_extractor_kwargs.setdefault("features_dim", ASSET_LSTM_UNITS2_POLICY)
89
+
90
+ super().__init__(
91
+ observation_space,
92
+ action_space,
93
+ lr_schedule,
94
+ net_arch=net_arch,
95
+ activation_fn=activation_fn, # Passando nn.ReLU
96
+ features_extractor_class=PortfolioFeaturesExtractorTorch,
97
+ features_extractor_kwargs=features_extractor_kwargs,
98
+ **kwargs,
99
+ )
agents/agents/data_handler_multi_asset.py ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # rnn/data_handler_multi_asset.py (NOVO ARQUIVO)
2
+
3
+
4
+ import pandas as pd
5
+ import numpy as np
6
+ import yfinance as yf # Ou ccxt, dependendo da sua preferência de fonte de dados
7
+ import pandas_ta as ta
8
+ from datetime import datetime, timedelta, timezone
9
+ from typing import List, Dict, Optional
10
+
11
+ WINDOW_SIZE = 60
12
+
13
+ # Importar do seu config.py
14
+ # Assumindo que config.py está em ../config.py ou rnn/config.py
15
+ # Ajuste o import conforme sua estrutura.
16
+ # Se train_rnn_model.py e este data_handler estiverem na mesma pasta 'scripts',
17
+ # e config.py estiver um nível acima:
18
+ # from ..app/config.py import (
19
+ # MULTI_ASSET_LIST, TIMEFRAME, DAYS_OF_DATA_TO_FETCH,
20
+ # # etc. para features a serem calculadas
21
+ # )
22
+ # Por agora, vamos definir aqui para exemplo:
23
+
24
+ # EXEMPLO DE CONFIGURAÇÃO (Mova para config.py depois)
25
+ MULTI_ASSET_SYMBOLS = {
26
+ 'eth': 'ETH-USD',
27
+ 'btc': 'BTC-USD',
28
+ 'ada': 'ADA-USD',
29
+ 'sol': 'SOL-USD'
30
+ } # Use os tickers corretos para yfinance ou ccxt
31
+ TIMEFRAME_YFINANCE = '1h' # yfinance suporta '1m', '2m', '5m', '15m', '30m', '60m', '90m', '1h', '1d', '5d', '1wk', '1mo', '3mo'
32
+ # Para '1h', yfinance só retorna os últimos 730 dias. Para mais dados, use '1d'.
33
+ # Se usar ccxt, TIMEFRAME = '1h' como antes.
34
+ DAYS_TO_FETCH = 365 * 2 # 2 anos
35
+
36
+ # Lista das features base que você quer calcular para CADA ativo
37
+ # (as 19 que definimos antes)
38
+ INDIVIDUAL_ASSET_BASE_FEATURES = [
39
+ 'open', 'high', 'low', 'close', 'volume', # OHLCV originais são necessários para os cálculos
40
+ 'sma_10', 'rsi_14', 'macd', 'macds', 'atr', 'bbp', 'cci_37', 'mfi_37', 'adx_14',
41
+ 'volume_zscore', 'body_size', 'body_size_norm_atr', 'body_vs_avg_body',
42
+ 'log_return', 'buy_condition_v1',
43
+ 'open_div_atr', 'high_div_atr',
44
+ 'low_div_atr',
45
+ 'close_div_atr',
46
+ 'volume_div_atr',
47
+ 'sma_10_div_atr' # 'sma_50' é calculada dentro de buy_condition_v1
48
+ # As colunas _div_atr serão criadas a partir destas
49
+ ]
50
+
51
+ # Features que serão normalizadas pelo ATR
52
+ COLS_TO_NORM_BY_ATR = ['open', 'high', 'low', 'close', 'volume', 'sma_10', 'macd', 'body_size']
53
+ #----------------------------------
54
+ # -- New get multi asset data for rl
55
+ # ... (imports e outras funções como antes) ...
56
+
57
+ def get_multi_asset_data_for_rl(
58
+ asset_symbols_map: Dict[str, str],
59
+ timeframe_yf: str,
60
+ days_to_fetch: int,
61
+ logger_instance # Adicionar logger como parâmetro
62
+ ) -> Optional[pd.DataFrame]: # Adicionar logger como parâmetro
63
+
64
+ all_asset_features_list: List[pd.DataFrame] = [] # Tipagem para clareza
65
+ min_data_length = float('inf')
66
+ print(all_asset_features_list)
67
+
68
+ #logger_instance.info(f"Iniciando get_multi_asset_data_for_rl para: {list(asset_symbols_map.keys())}")
69
+
70
+ for asset_key, yf_ticker in asset_symbols_map.items():
71
+ #logger_instance.info(f"\n--- Processando {asset_key} ({yf_ticker}) ---")
72
+ # ... (lógica de fetch_single_asset_ohlcv_yf como antes) ...
73
+ single_asset_ohlcv = fetch_single_asset_ohlcv_yf(yf_ticker, period=f"{days_to_fetch}d", interval=timeframe_yf) # Passar logger se a função aceitar
74
+
75
+ # if single_asset_ohlcv.empty:
76
+ # logger_instance.warning(f"Sem dados OHLCV para {yf_ticker}, pulando.")
77
+ # continue
78
+
79
+ single_asset_features = calculate_all_features_for_single_asset(single_asset_ohlcv)#, logger_instance)
80
+
81
+ if single_asset_features is None or single_asset_features.empty:
82
+ logger_instance.warning(f"Sem features calculadas para {yf_ticker}, pulando.")
83
+ continue
84
+
85
+ #logger_instance.info(f"Features para {asset_key} shape: {single_asset_features.shape}, Index Min: {single_asset_features.index.min()}, Index Max: {single_asset_features.index.max()}")
86
+
87
+ # Garantir que o índice é DatetimeIndex e UTC para todos antes de adicionar prefixo e à lista
88
+ if not isinstance(single_asset_features.index, pd.DatetimeIndex):
89
+ single_asset_features.index = pd.to_datetime(single_asset_features.index)
90
+ if single_asset_features.index.tz is None:
91
+ single_asset_features.index = single_asset_features.index.tz_localize('UTC')
92
+ else:
93
+ single_asset_features.index = single_asset_features.index.tz_convert('UTC')
94
+
95
+ single_asset_features = single_asset_features.add_prefix(f"{asset_key}_")
96
+ all_asset_features_list.append(single_asset_features)
97
+ min_data_length = min(min_data_length, len(single_asset_features))
98
+
99
+ if not all_asset_features_list:
100
+ logger_instance.error("Nenhum DataFrame de feature de ativo foi adicionado à lista.")
101
+ return None
102
+
103
+ if min_data_length == float('inf') or min_data_length < WINDOW_SIZE: # Adicionada checagem de WINDOW_SIZE
104
+ logger_instance.error(f"min_data_length inválido ({min_data_length}) ou menor que WINDOW_SIZE ({WINDOW_SIZE}). Não é possível truncar/usar.")
105
+ return None
106
+
107
+ #logger_instance.info(f"Menor número de linhas de dados encontrado (min_data_length): {min_data_length}")
108
+
109
+ # Truncar para garantir que todos os DFs tenham o mesmo comprimento ANTES do concat
110
+ # E que tenham pelo menos min_data_length.
111
+ # É importante que os ÍNDICES de data/hora se sobreponham para o join='inner' funcionar.
112
+ # Apenas pegar o .tail() pode não alinhar os timestamps se os DFs tiverem começos diferentes.
113
+
114
+ # Melhor abordagem: encontrar o índice comum mais recente e o mais antigo.
115
+ if not all_asset_features_list: # Checagem se a lista não ficou vazia por algum motivo
116
+ logger_instance.error("all_asset_features_list está vazia antes do alinhamento de índice.")
117
+ return None
118
+ print(all_asset_features_list)
119
+ # Alinhar DataFrames por um índice comum antes de concatenar
120
+ # 1. Encontrar o primeiro timestamp comum a todos
121
+ # 2. Encontrar o último timestamp comum a todos
122
+ # Ou, mais simples, confiar no join='inner' do concat, mas garantir que os DFs são válidos.
123
+
124
+ # Vamos simplificar e manter o truncamento pelo tail, mas com mais logs
125
+ # e garantir que são DataFrames.
126
+
127
+ truncated_asset_features_list = []
128
+ for i, df_asset in enumerate(all_asset_features_list):
129
+ if isinstance(df_asset, pd.DataFrame) and len(df_asset) >= min_data_length:
130
+ truncated_df = df_asset.tail(min_data_length)
131
+ #logger_instance.info(f" DF truncado {i} ({df_asset.columns[0].split('_')[0]}): shape {truncated_df.shape}, ")
132
+ #f"Index Min: {truncated_df.index.min()}, Max: {truncated_df.index.max()}")
133
+ truncated_asset_features_list.append(truncated_df)
134
+ else:
135
+ asset_name_debug = df_asset.columns[0].split('_')[0] if isinstance(df_asset, pd.DataFrame) and not df_asset.empty else f"DF_{i}"
136
+ logger_instance.warning(f" DF {asset_name_debug} inválido ou muito curto (len: {len(df_asset) if isinstance(df_asset, pd.DataFrame) else 'N/A'}) para truncamento. Pulando.")
137
+
138
+ if not truncated_asset_features_list:
139
+ #ogger_instance.error("Nenhum DataFrame válido restou após truncamento para concatenar.")
140
+ return None
141
+
142
+ # Se houver apenas UM DataFrame na lista, não precisa concatenar, apenas retorna ele.
143
+ if len(truncated_asset_features_list) == 1:
144
+ #logger_instance.info("Apenas um DataFrame de ativo processado, retornando-o diretamente.")
145
+ combined_df = truncated_asset_features_list[0]
146
+ else:
147
+ #logger_instance.info(f"Concatenando {len(truncated_asset_features_list)} DataFrames de ativos com join='inner'...")
148
+ try:
149
+ combined_df = pd.concat(truncated_asset_features_list, axis=1, join='outer')
150
+ print(combined_df)
151
+ except Exception as e_concat:
152
+ logger_instance.error(f"ERRO CRÍTICO durante pd.concat: {e_concat}", exc_info=True)
153
+ return None
154
+
155
+
156
+ combined_df.fillna(method='ffill', inplace=True)
157
+ # Depois do ffill, ainda pode haver NaNs no início se algum ativo começar depois dos outros.
158
+ if not combined_df.empty:
159
+ #logger_instance.info(f"Shape após ffill: {combined_df.shape}. Buscando primeiro/último índice válido...")
160
+ first_valid_index = combined_df.first_valid_index()
161
+ last_valid_index = combined_df.last_valid_index()
162
+
163
+ if pd.isna(first_valid_index) or pd.isna(last_valid_index):
164
+ print("Não foi possível determinar first/last_valid_index após ffill.")
165
+ return None
166
+
167
+ print(f"Primeiro índice válido: {first_valid_index}, Último índice válido: {last_valid_index}")
168
+ combined_df = combined_df.loc[first_valid_index:last_valid_index]
169
+ print(f"Shape após fatiar por first/last valid index: {combined_df.shape}")
170
+
171
+ # Um dropna final pode ser necessário se o ffill não pegou tudo (improvável, mas seguro)
172
+ combined_df.dropna(inplace=True)
173
+ print(f"Shape após dropna final (pós-fatiamento): {combined_df.shape}")
174
+ print("Imprimindo DF_COMBINED com index ")
175
+ print(combined_df)
176
+
177
+ #logger_instance.info(f"Tipo de combined_df após concat: {type(combined_df)}")
178
+ if not isinstance(combined_df, pd.DataFrame):
179
+ logger_instance.error(f"combined_df NÃO é um DataFrame após concat. Tipo: {type(combined_df)}")
180
+ return None
181
+
182
+
183
+ return combined_df
184
+ # if combined_df.empty:
185
+
186
+ # logger_instance.error("DataFrame combinado está VAZIO após concatenação e join='inner'. "
187
+ # "Isso geralmente significa que não há timestamps comuns entre TODOS os ativos processados.")
188
+ # # Adicionar mais depuração aqui se isso acontecer:
189
+ # # for i, df_trunc_debug in enumerate(truncated_asset_features_list):
190
+ # # logger_instance.info(f"Debug DF {i} - Head:\n{df_trunc_debug.head(3)}")
191
+ # # logger_instance.info(f"Debug DF {i} - Tail:\n{df_trunc_debug.tail(3)}")
192
+ # # logger_instance.info(f"DataFrame combinado ANTES do dropna final, shape: {combined_df.shape}")
193
+ # print(combined_df.head()) # Descomente para depuração pesada
194
+ # return None
195
+
196
+
197
+
198
+
199
+ # ...
200
+ #----------------------------------
201
+
202
+ # def get_multi_asset_data_for_rl(
203
+ # asset_symbols_map: Dict[str, str],
204
+ # timeframe_yf: str,
205
+ # days_to_fetch: int
206
+ # ) -> Optional[pd.DataFrame]:
207
+ # """
208
+ # Busca, processa e combina dados de múltiplos ativos em um DataFrame achatado.
209
+ # """
210
+ # all_asset_features_list = []
211
+ # # min_data_length = float('inf') # Inicializar com um valor alto
212
+ # # Vamos inicializar com 0 e pegar o len do primeiro DF válido
213
+ # min_data_length = 0
214
+ # first_valid_df_processed = False
215
+
216
+ # for asset_key, yf_ticker in asset_symbols_map.items():
217
+ # print(f"\n--- Processando {asset_key} ({yf_ticker}) ---")
218
+ # period_yf = f"{days_to_fetch}d"
219
+ # if timeframe_yf == '1h' and days_to_fetch > 730:
220
+ # print(f"AVISO: Para {timeframe_yf}, buscando no máximo 730 dias com yfinance para {yf_ticker}.")
221
+ # period_yf = "730d"
222
+
223
+ # single_asset_ohlcv = fetch_single_asset_ohlcv_yf(yf_ticker, period=period_yf, interval=timeframe_yf)
224
+ # if single_asset_ohlcv.empty:
225
+ # print(f"AVISO: Sem dados OHLCV para {yf_ticker}, pulando este ativo.")
226
+ # continue
227
+
228
+ # single_asset_features = calculate_all_features_for_single_asset(single_asset_ohlcv) # Passar logger se tiver
229
+ # if single_asset_features is None or single_asset_features.empty:
230
+ # print(f"AVISO: Sem features calculadas para {yf_ticker}, pulando este ativo.")
231
+ # continue
232
+
233
+ # single_asset_features = single_asset_features.add_prefix(f"{asset_key}_")
234
+ # all_asset_features_list.append(single_asset_features)
235
+
236
+ # # Atualizar min_data_length de forma mais segura
237
+ # if not first_valid_df_processed:
238
+ # min_data_length = len(single_asset_features)
239
+ # first_valid_df_processed = True
240
+ # else:
241
+ # min_data_length = min(min_data_length, len(single_asset_features))
242
+
243
+ # if not all_asset_features_list: # Se nenhum ativo foi processado com sucesso
244
+ # print("ERRO: Nenhum dado de feature de ativo foi processado com sucesso para a lista `all_asset_features_list`.")
245
+ # return None # Retorna None, que não tem '.empty' mas será checado por 'is None'
246
+
247
+ # if min_data_length == 0 : # Checagem adicional se algo deu muito errado
248
+ # print("ERRO: min_data_length é zero após processar ativos, não é possível truncar DataFrames.")
249
+ # return None
250
+
251
+ # print(f"Menor número de linhas de dados encontrado entre os ativos (min_data_length): {min_data_length}")
252
+ # truncated_asset_features_list = [df.tail(min_data_length) for df in all_asset_features_list if not df.empty and len(df) >= min_data_length]
253
+
254
+ # # Verificar se a lista de DFs truncados não está vazia ANTES de concatenar
255
+ # if not truncated_asset_features_list:
256
+ # print("ERRO: Nenhum DataFrame válido restou após o truncamento. Não é possível concatenar.")
257
+ # return None
258
+
259
+ # print(f"Concatenando {len(truncated_asset_features_list)} DataFrames de ativos...")
260
+ # try:
261
+ # combined_df = pd.concat(truncated_asset_features_list, axis=1, join='inner')
262
+ # except Exception as e_concat:
263
+ # print(f"ERRO durante pd.concat: {e_concat}")
264
+ # return None # Retorna None em caso de erro na concatenação
265
+
266
+ # # Agora, combined_df DEVE ser um DataFrame (mesmo que vazio se o join falhar)
267
+ # if not isinstance(combined_df, pd.DataFrame):
268
+ # print(f"ERRO: pd.concat não retornou um DataFrame. Tipo retornado: {type(combined_df)}")
269
+ # return None
270
+
271
+ # if combined_df.empty: # Esta checagem agora deve funcionar ou ser desnecessária se o anterior já retornou None
272
+ # print("ERRO: DataFrame combinado está vazio após concatenação e join. Verifique os dados dos ativos e o alinhamento de datas.")
273
+ # return None
274
+
275
+ # combined_df.dropna(inplace=True)
276
+
277
+ # if combined_df.empty: # Checagem final após dropna
278
+ # print("ERRO: DataFrame combinado está vazio após dropna final.")
279
+ # return None
280
+
281
+ # print(f"\nDataFrame multi-ativo final gerado com shape: {combined_df.shape}")
282
+ # return combined_df
283
+
284
+
285
+
286
+
287
+ def fetch_single_asset_ohlcv_yf(ticker_symbol: str, period: str = "2y", interval: str = "1h") -> pd.DataFrame:
288
+ """ Adaptação da sua função fetch_historical_ohlcv de financial_data_agent.py """
289
+ print(f"Buscando dados para {ticker_symbol} com yfinance (period: {period}, interval: {interval})...")
290
+ try:
291
+ ticker = yf.Ticker(ticker_symbol)
292
+ # Para dados horários, o período máximo é geralmente 730 dias com yfinance
293
+ # Se precisar de mais, considere '1d' e depois reamostre, ou use ccxt para cripto.
294
+ if interval == '1h' and period.endswith('y') and int(period[:-1]) * 365 > 730:
295
+ print(f"AVISO: yfinance pode limitar dados horários a 730 dias. Buscando 'max' para {interval} e depois fatiando.")
296
+ data = ticker.history(interval=interval, period="730d") # Pega o máximo possível
297
+ elif interval == '1d' and period.endswith('y'):
298
+ data = ticker.history(period=period, interval=interval)
299
+ else: # Para períodos menores ou outros intervalos
300
+ data = ticker.history(period=period, interval=interval)
301
+
302
+ if data.empty:
303
+ print(f"Nenhum dado encontrado para {ticker_symbol}.")
304
+ return pd.DataFrame()
305
+
306
+ data.rename(columns={
307
+ "Open": "open", "High": "high", "Low": "low",
308
+ "Close": "close", "Volume": "volume", "Adj Close": "adj_close"
309
+ }, inplace=True)
310
+
311
+ # Selecionar apenas as colunas OHLCV e garantir que o índice é DatetimeIndex UTC
312
+ data = data[['open', 'high', 'low', 'close', 'volume']]
313
+ if data.index.tz is None:
314
+ data.index = data.index.tz_localize('UTC')
315
+ else:
316
+ data.index = data.index.tz_convert('UTC')
317
+
318
+ # Para dados horários, yfinance pode retornar dados do fim de semana (sem volume)
319
+ # e o último candle pode estar incompleto.
320
+ # if interval == '1h':
321
+ # data = data[data['volume'] > 0] # Remover candles sem volume
322
+ # data = data[:-1] # Remover o último candle que pode estar incompleto
323
+
324
+ print(f"Dados coletados para {ticker_symbol}: {len(data)} linhas.")
325
+ return data
326
+ except Exception as e:
327
+ print(f"Erro ao buscar dados para {ticker_symbol} com yfinance: {e}")
328
+ return pd.DataFrame()
329
+
330
+
331
+ def calculate_all_features_for_single_asset(ohlcv_df: pd.DataFrame) -> Optional[pd.DataFrame]:
332
+ """Calcula todas as features base para um único ativo."""
333
+ if ohlcv_df.empty: return None
334
+ df = ohlcv_df.copy()
335
+ print(f"Calculando features para ativo (shape inicial: {df.shape})...")
336
+
337
+ # GARANTIR que a coluna 'close' original será preservada no DataFrame final
338
+ if 'close' in df.columns:
339
+ df['close'] = df['close'] # redundante, mas deixa explícito que não será sobrescrita
340
+
341
+ if ta:
342
+ df.ta.sma(length=10, close='close', append=True, col_names=('sma_10',))
343
+ df.ta.rsi(length=14, close='close', append=True, col_names=('rsi_14',))
344
+ macd_out = df.ta.macd(close='close', append=False)
345
+ if macd_out is not None and not macd_out.empty:
346
+ df['macd'] = macd_out.iloc[:,0]
347
+ df['macds'] = macd_out.iloc[:,2] # Linha de sinal para buy_condition
348
+ df.ta.atr(length=14, append=True, col_names=('atr',))
349
+ df.ta.bbands(length=20, close='close', append=True, col_names=('bbl', 'bbm', 'bbu', 'bbb', 'bbp'))
350
+ df.ta.cci(length=37, append=True, col_names=('cci_37',))
351
+ df['volume'] = df['volume'].astype(float)
352
+ df.ta.mfi(length=37, close='close', high='high', low='low', volume='volume', append=True, col_names=('mfi_37',))
353
+ df.ta.mfi(length=37, append=True, col_names=('mfi_37',))
354
+ adx_out = df.ta.adx(length=14, append=False)
355
+ if adx_out is not None and not adx_out.empty:
356
+ df['adx_14'] = adx_out.iloc[:,0]
357
+
358
+ rolling_vol_mean = df['volume'].rolling(window=20).mean()
359
+ rolling_vol_std = df['volume'].rolling(window=20).std()
360
+ df['volume_zscore'] = (df['volume'] - rolling_vol_mean) / (rolling_vol_std + 1e-9)
361
+
362
+ df['body_size'] = abs(df['close'] - df['open'])
363
+
364
+ # ATR precisa existir para as próximas. Drop NaNs do ATR primeiro.
365
+ df.dropna(subset=['atr'], inplace=True)
366
+ df_atr_valid = df[df['atr'] > 1e-9].copy()
367
+ if df_atr_valid.empty:
368
+ print("AVISO: ATR inválido para todas as linhas restantes, features _div_atr e body_size_norm_atr podem ser todas NaN ou vazias.")
369
+ # Criar colunas com NaN para manter a estrutura
370
+ df['body_size_norm_atr'] = np.nan
371
+ for col in COLS_TO_NORM_BY_ATR:
372
+ df[f'{col}_div_atr'] = np.nan
373
+ else:
374
+ df['body_size_norm_atr'] = df['body_size'] / df['atr'] # ATR já filtrado para > 1e-9
375
+ for col in COLS_TO_NORM_BY_ATR:
376
+ if col in df.columns:
377
+ df[f'{col}_div_atr'] = df[col] / (df['atr'] + 1e-9) # Adicionar 1e-9 aqui também por segurança
378
+ else:
379
+ df[f'{col}_div_atr'] = np.nan
380
+
381
+
382
+ df['body_vs_avg_body'] = df['body_size'] / (df['body_size'].rolling(window=20).mean() + 1e-9)
383
+ df['log_return'] = np.log(df['close'] / df['close'].shift(1))
384
+
385
+ sma_50_series = df.ta.sma(length=50, close='close', append=False)
386
+ if sma_50_series is not None: df['sma_50'] = sma_50_series
387
+ else: df['sma_50'] = np.nan
388
+
389
+ if all(col in df.columns for col in ['macd', 'macds', 'rsi_14', 'close', 'sma_50']):
390
+ df['buy_condition_v1'] = ((df['macd'] > df['macds']) & (df['rsi_14'] > 50) & (df['close'] > df['sma_50'])).astype(int)
391
+ else:
392
+ df['buy_condition_v1'] = 0
393
+
394
+ # Selecionar apenas as colunas que realmente usaremos como features base para o modelo
395
+ # (incluindo as _div_atr e as originais que não foram normalizadas por ATR)
396
+ # Esta lista de features é a que será passada para os scalers no script de treino.
397
+ # E também as colunas que o rnn_predictor.py precisará ter antes de aplicar seus scalers.
398
+ # Esta lista deve vir do config.py (BASE_FEATURE_COLS)
399
+
400
+ # Exemplo:
401
+ # final_feature_columns = [
402
+ # 'open_div_atr', 'high_div_atr', 'low_div_atr', 'close_div_atr', 'volume_div_atr',
403
+ # 'log_return', 'rsi_14', 'atr', 'bbp', 'cci_37', 'mfi_37',
404
+ # 'body_size_norm_atr', 'body_vs_avg_body', 'macd', 'sma_10_div_atr',
405
+ # 'adx_14', 'volume_zscore', 'buy_condition_v1'
406
+ # ] # Esta é a BASE_FEATURE_COLS do seu config.py
407
+
408
+ # Verificar se todas as colunas em INDIVIDUAL_ASSET_BASE_FEATURES existem
409
+ # (INDIVIDUAL_ASSET_BASE_FEATURES deve ser igual a config.BASE_FEATURE_COLS)
410
+ current_feature_cols = [col for col in INDIVIDUAL_ASSET_BASE_FEATURES if col in df.columns]
411
+ missing_cols = [col for col in INDIVIDUAL_ASSET_BASE_FEATURES if col not in df.columns]
412
+ if missing_cols:
413
+ print(f"AVISO: Colunas de features ausentes após cálculo: {missing_cols}. Usando apenas as disponíveis: {current_feature_cols}")
414
+ # GARANTIR que 'close' (preço original) está presente nas features finais
415
+ if 'close' not in current_feature_cols and 'close' in df.columns:
416
+ current_feature_cols.append('close')
417
+
418
+ df_final_features = df[current_feature_cols].copy()
419
+ df_final_features.dropna(inplace=True)
420
+ print(f"Features calculadas. Shape após dropna: {df_final_features.shape}. Colunas: {df_final_features.columns.tolist()}")
421
+ return df_final_features
422
+ else:
423
+ print("pandas_ta não está disponível.")
424
+ return None
425
+
426
+
427
+ if __name__ == '__main__':
428
+ print("Testando data_handler_multi_asset.py...")
429
+ # Substitua pelos tickers yfinance reais que você quer usar
430
+ test_assets = {
431
+ 'eth': 'ETH-USD',
432
+ 'btc': 'BTC-USD',
433
+ # 'aapl': 'AAPL' # Exemplo de ação
434
+ }
435
+ multi_asset_data = get_multi_asset_data_for_rl(
436
+ test_assets,
437
+ timeframe_yf='1h', # Para teste rápido, período menor
438
+ days_to_fetch=90 # Para teste rápido, período menor
439
+ )
440
+
441
+ if multi_asset_data is not None and not multi_asset_data.empty:
442
+ print("\n--- Exemplo do DataFrame Multi-Ativo Gerado ---")
443
+ print(multi_asset_data.head())
444
+ print(f"\nShape: {multi_asset_data.shape}")
445
+ print(f"\nInfo:")
446
+ multi_asset_data.info()
447
+ else:
448
+ print("\nFalha ao gerar DataFrame multi-ativo.")
agents/agents/dataset_update_agent.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # agents/dataset_update_agent.py
2
+ import pandas as pd
3
+ from datetime import datetime
4
+
5
+ def update_dataset(path="data/sp500_news.csv", new_data=pd.DataFrame()):
6
+ df = pd.read_csv(path)
7
+ combined = pd.concat([df, new_data]).drop_duplicates().reset_index(drop=True)
8
+ combined.to_csv(path, index=False)
9
+ print(f"Dataset atualizado: {path}")
agents/agents/deep_portfolio.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import tensorflow as tf
3
+ from tensorflow.keras.layers import LSTM, Dense, Conv1D, MultiHeadAttention
4
+ from transformers import AutoTokenizer, TFAutoModelForSequenceClassification # Changed here
5
+ import gymnasium as gym
6
+ from gymnasium import spaces
7
+
8
+ class DeepPortfolioAI(tf.keras.Model):
9
+ def __init__(self, num_assets, sequence_length=60):
10
+ super(DeepPortfolioAI, self).__init__()
11
+
12
+ # Parâmetros do modelo
13
+ self.num_assets = num_assets
14
+ self.sequence_length = sequence_length
15
+
16
+ # CNN para análise de padrões técnicos
17
+ self.conv1 = Conv1D(64, 3, activation='relu')
18
+ self.conv2 = Conv1D(128, 3, activation='relu')
19
+
20
+ # LSTM para análise temporal
21
+ self.lstm1 = LSTM(128, return_sequences=True)
22
+ self.lstm2 = LSTM(64)
23
+
24
+ # Attention para correlações entre ativos
25
+ self.attention = MultiHeadAttention(num_heads=8, key_dim=64)
26
+
27
+ # Camadas densas para decisão final
28
+ self.dense1 = Dense(256, activation='relu')
29
+ self.dense2 = Dense(128, activation='relu')
30
+ self.output_layer = Dense(num_assets, activation='softmax')
31
+
32
+ # Inicializar tokenizer e modelo de sentimento
33
+ self.tokenizer = AutoTokenizer.from_pretrained('ProsusAI/finbert')
34
+ self.sentiment_model = TFAutoModelForSequenceClassification.from_pretrained('ProsusAI/finbert') # Changed here
35
+
36
+ def call(self, inputs):
37
+ market_data, news_data = inputs
38
+
39
+ # Análise técnica com CNN
40
+ x_technical = self.conv1(market_data)
41
+ x_technical = self.conv2(x_technical)
42
+
43
+ # Análise temporal com LSTM
44
+ x_temporal = self.lstm1(x_technical)
45
+ x_temporal = self.lstm2(x_temporal)
46
+
47
+ # Attention para correlações
48
+ x_attention = self.attention(x_temporal, x_temporal, x_temporal)
49
+
50
+ # Combinar com análise de sentimento
51
+ sentiment_embeddings = self._process_news(news_data)
52
+ x_combined = tf.concat([x_attention, sentiment_embeddings], axis=-1)
53
+
54
+ # Camadas densas finais
55
+ x = self.dense1(x_combined)
56
+ x = self.dense2(x)
57
+ return self.output_layer(x)
58
+
59
+ def _process_news(self, news_data):
60
+ inputs = self.tokenizer(news_data, return_tensors="pt", padding=True, truncation=True)
61
+ sentiment_scores = self.sentiment_model(**inputs).logits
62
+ return tf.convert_to_tensor(sentiment_scores.detach().numpy())
63
+
64
+ class PortfolioEnvironment(gym.Env):
65
+ def __init__(self, data, initial_balance=100000):
66
+ super(PortfolioEnvironment, self).__init__()
67
+
68
+ self.data = data
69
+ self.initial_balance = initial_balance
70
+ self.current_step = 0
71
+
72
+ # Define espaços de ação e observação
73
+ self.action_space = spaces.Box(
74
+ low=0, high=1, shape=(len(data.columns),), dtype=np.float32)
75
+ self.observation_space = spaces.Box(
76
+ low=-np.inf, high=np.inf, shape=(60, len(data.columns)), dtype=np.float32)
77
+
78
+ def reset(self):
79
+ self.current_step = 0
80
+ self.balance = self.initial_balance
81
+ self.portfolio = np.zeros(len(self.data.columns))
82
+ return self._get_observation()
83
+
84
+ def step(self, action):
85
+ # Implementar lógica de negociação
86
+ current_prices = self.data.iloc[self.current_step]
87
+ next_prices = self.data.iloc[self.current_step + 1]
88
+
89
+ # Calcular retorno
90
+ returns = (next_prices - current_prices) / current_prices
91
+ reward = np.sum(action * returns)
92
+
93
+ # Atualizar portfolio
94
+ self.portfolio = action
95
+ self.balance *= (1 + reward)
96
+
97
+ # Incrementar step
98
+ self.current_step += 1
99
+ done = self.current_step >= len(self.data) - 1
100
+
101
+ return self._get_observation(), reward, done, {}
102
+
103
+ def _get_observation(self):
104
+ return self.data.iloc[self.current_step-60:self.current_step].values
agents/agents/deep_portfolio_torch.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ import torch.nn.functional as F
4
+
5
+ class SingleAssetProcessor(nn.Module):
6
+ def __init__(self, sequence_length, num_features_per_asset, cnn_filters1, cnn_filters2, lstm_units):
7
+ super().__init__()
8
+ self.conv1 = nn.Conv1d(num_features_per_asset, cnn_filters1, kernel_size=3, padding=1)
9
+ self.conv2 = nn.Conv1d(cnn_filters1, cnn_filters2, kernel_size=3, padding=1)
10
+ self.lstm = nn.LSTM(input_size=cnn_filters2, hidden_size=lstm_units, batch_first=True)
11
+
12
+ def forward(self, x):
13
+ # x: (batch, seq_len, num_features_per_asset)
14
+ x = x.transpose(1, 2) # (batch, num_features_per_asset, seq_len)
15
+ x = F.relu(self.conv1(x))
16
+ x = F.relu(self.conv2(x))
17
+ x = x.transpose(1, 2) # (batch, seq_len, cnn_filters2)
18
+ _, (h_n, _) = self.lstm(x) # h_n: (1, batch, lstm_units)
19
+ return h_n.squeeze(0) # (batch, lstm_units)
20
+
21
+ class DeepPortfolioAgentNetworkTorch(nn.Module):
22
+ def __init__(self, num_assets, sequence_length, num_features_per_asset,
23
+ asset_cnn_filters1=32, asset_cnn_filters2=64,
24
+ asset_lstm_units1=64, asset_lstm_units2=32,
25
+ final_dense_units1=128, final_dense_units2=32,
26
+ final_dropout=0.3, mha_num_heads=4, mha_key_dim_divisor=2,
27
+ output_latent_features=True, use_sentiment_analysis=False):
28
+ super().__init__()
29
+ self.num_assets = num_assets
30
+ self.sequence_length = sequence_length
31
+ self.num_features_per_asset = num_features_per_asset
32
+ self.output_latent_features = output_latent_features
33
+ self.use_sentiment_analysis = use_sentiment_analysis
34
+
35
+ # Processador individual de ativos
36
+ self.asset_processor = SingleAssetProcessor(
37
+ sequence_length, num_features_per_asset,
38
+ asset_cnn_filters1, asset_cnn_filters2, asset_lstm_units1
39
+ )
40
+ # Atenção multi-cabeça
41
+ self.attention = nn.MultiheadAttention(
42
+ embed_dim=asset_lstm_units1,
43
+ num_heads=mha_num_heads,
44
+ batch_first=True
45
+ )
46
+ # Pooling global
47
+ self.global_avg_pool = nn.AdaptiveAvgPool1d(1)
48
+ # Camadas densas finais
49
+ self.dense1 = nn.Linear(asset_lstm_units1, final_dense_units1)
50
+ self.dropout1 = nn.Dropout(final_dropout)
51
+ self.dense2 = nn.Linear(final_dense_units1, final_dense_units2)
52
+ self.dropout2 = nn.Dropout(final_dropout)
53
+ self.output_allocation = nn.Linear(final_dense_units2, num_assets)
54
+
55
+ def forward(self, x):
56
+ # x: (batch, seq_len, num_assets * num_features_per_asset)
57
+ batch_size = x.size(0)
58
+ # Separar cada ativo
59
+ asset_representations = []
60
+ for i in range(self.num_assets):
61
+ start = i * self.num_features_per_asset
62
+ end = (i + 1) * self.num_features_per_asset
63
+ asset_data = x[:, :, start:end] # (batch, seq_len, num_features_per_asset)
64
+ asset_repr = self.asset_processor(asset_data) # (batch, lstm_units1)
65
+ asset_representations.append(asset_repr)
66
+ # Empilhar ativos: (batch, num_assets, lstm_units1)
67
+ stacked = torch.stack(asset_representations, dim=1)
68
+ # Atenção multi-cabeça
69
+ attn_output, _ = self.attention(stacked, stacked, stacked)
70
+ # Pooling global sobre ativos (num_assets)
71
+ pooled = self.global_avg_pool(attn_output.transpose(1,2)).squeeze(-1) # (batch, lstm_units1)
72
+ # Camadas densas finais
73
+ x = F.relu(self.dense1(pooled))
74
+ x = self.dropout1(x)
75
+ x = F.relu(self.dense2(x))
76
+ x = self.dropout2(x)
77
+ if self.output_latent_features:
78
+ return x # (batch, final_dense_units2)
79
+ else:
80
+ return F.softmax(self.output_allocation(x), dim=-1) # (batch, num_assets)
agents/agents/financial_data_agent.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # agents/financial_data_agent.py
2
+ import yfinance as yf
3
+ import pandas as pd
4
+ from agno.agent import Agent # Commented out
5
+ from agno.models.anthropic import Claude # Commented out
6
+ from agno.tools.yfinance import YFinanceTools # Commented out
7
+
8
+ def get_stock_report(ticker="NVDA"): # Commented out
9
+ agent = Agent(
10
+ model=Claude(id="claude-3-7-sonnet-latest"),
11
+ tools=[
12
+ YFinanceTools(
13
+ stock_price=True,
14
+ analyst_recommendations=True,
15
+ company_info=True,
16
+ company_news=True,
17
+ )
18
+ ],
19
+ instructions=[
20
+ "Use tables to display data",
21
+ "Only output the report, no other text",
22
+ ],
23
+ markdown=True,
24
+ )
25
+ return agent.get_response(f"Write a financial report on {ticker}")
26
+
27
+ def fetch_historical_ohlcv(ticker_symbol: str, period: str = "1y", interval: str = "1d") -> pd.DataFrame:
28
+ """
29
+ Fetches historical OHLCV data for a given ticker symbol.
30
+
31
+ Args:
32
+ ticker_symbol (str): The stock ticker symbol (e.g., "AAPL" for Apple on NASDAQ,
33
+ "PETR4.SA" for Petrobras on B3, "000001.SS" for SSE Composite Index).
34
+ period (str): The period for which to download data (e.g., "1d", "5d", "1mo", "3mo", "6mo", "1y", "2y", "5y", "10y", "ytd", "max").
35
+ interval (str): The interval of data points (e.g., "1m", "2m", "5m", "15m", "30m", "60m", "90m", "1h", "1d", "5d", "1wk", "1mo", "3mo").
36
+
37
+ Returns:
38
+ pd.DataFrame: A pandas DataFrame containing the OHLCV data, or an empty DataFrame if an error occurs.
39
+ """
40
+ try:
41
+ ticker = yf.Ticker(ticker_symbol)
42
+ data = ticker.history(period=period, interval=interval)
43
+ if data.empty:
44
+ print(f"No data found for {ticker_symbol} for the given period/interval.")
45
+ return pd.DataFrame()
46
+ # Ensure column names are consistent (Yahoo Finance sometimes uses 'Adj Close')
47
+ data.rename(columns={"Adj Close": "Adj_Close"}, inplace=True)
48
+ return data
49
+ except Exception as e:
50
+ print(f"Error fetching data for {ticker_symbol}: {e}")
51
+ return pd.DataFrame()
52
+
53
+ if __name__ == '__main__':
54
+ # Example usage:
55
+ # NASDAQ
56
+ aapl_data = fetch_historical_ohlcv("AAPL", period="1mo", interval="1d")
57
+ if not aapl_data.empty:
58
+ print("\nAAPL Data (NASDAQ):")
59
+ print(aapl_data.head())
60
+
61
+ # B3 (Brazilian Stock Exchange) - Example: Petrobras
62
+ petr4_data = fetch_historical_ohlcv("PETR4.SA", period="1mo", interval="1d")
63
+ if not petr4_data.empty:
64
+ print("\nPETR4.SA Data (B3):")
65
+ print(petr4_data.head())
66
+
67
+ # Asian Market - Example: Samsung Electronics (Korea Exchange)
68
+ samsung_data = fetch_historical_ohlcv("005930.KS", period="1mo", interval="1d")
69
+ if not samsung_data.empty:
70
+ print("\n005930.KS Data (Samsung - KRX):")
71
+ print(samsung_data.head())
72
+
73
+ # Example for a non-existent ticker or error
74
+ error_data = fetch_historical_ohlcv("NONEXISTENTTICKER", period="1d")
75
+ if error_data.empty:
76
+ print("\nSuccessfully handled non-existent ticker.")
agents/agents/investment_agent.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # agents/investment_agent.py
2
+ def execute_investment(prediction, threshold=0.8):
3
+ if prediction > threshold:
4
+ print("Comprar ativos com probabilidade:", prediction)
5
+ # chamada API para execução real
6
+ else:
7
+ print("Não investir. Probabilidade baixa:", prediction)
agents/agents/portfolio_environment.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # agents/portfolio_environment.py (ou atcoin_env.py)
2
+
3
+ from typing import List
4
+ import numpy as np
5
+ import pandas as pd
6
+ import gymnasium as gym
7
+ from gymnasium import spaces
8
+ from collections import deque
9
+
10
+ # Importar do config.py
11
+ # from ..config import WINDOW_SIZE # Ajuste o import
12
+ WINDOW_SIZE_ENV = 60 # Exemplo, pegue do config
13
+ NUM_ASSETS=4
14
+ WINDOW_SIZE=60
15
+
16
+ NUM_FEATURES_PER_ASSET=26
17
+
18
+
19
+
20
+
21
+
22
+
23
+ class PortfolioEnv(gym.Env): # Renomeado para seguir convenção de Gymnasium (Opcional)
24
+ metadata = {'render_modes': ['human'], 'render_fps': 30}
25
+
26
+ def __init__(self, df_multi_asset_features: pd.DataFrame,
27
+ asset_symbols_list: List[str], # Lista de chaves dos ativos ex: ['crypto_eth', 'stock_aapl']
28
+ initial_balance=100000,
29
+ window_size=WINDOW_SIZE_ENV,
30
+ transaction_cost_pct=0.001,
31
+ reward_window_size=240, # Janela para cálculo do Sharpe Ratio (ex: 60 passos/horas)
32
+ risk_free_rate_per_step=None): # Custo de transação de 0.1%
33
+ super(PortfolioEnv, self).__init__()
34
+
35
+ self.df = df_multi_asset_features.copy() # DataFrame ACHATADO com todas as features de todos os ativos
36
+ self.asset_keys = asset_symbols_list # Usado para identificar colunas de preço de fechamento
37
+ self.num_assets = len(asset_symbols_list)
38
+ self.initial_balance = initial_balance
39
+ self.window_size = window_size
40
+ self.transaction_cost_pct = transaction_cost_pct
41
+ self.reward_window_size = reward_window_size
42
+ # Cálculo automático da taxa livre de risco por passo se não for passada
43
+ RISK_FREE_RATE_ANNUAL = 0.02 # 2% ao ano
44
+ TRADING_DAYS_PER_YEAR = 252
45
+ HOURS_PER_DAY_TRADING = 24
46
+ if risk_free_rate_per_step is None:
47
+ self.risk_free_rate_per_step = RISK_FREE_RATE_ANNUAL / (TRADING_DAYS_PER_YEAR * HOURS_PER_DAY_TRADING)
48
+ else:
49
+ self.risk_free_rate_per_step = risk_free_rate_per_step
50
+ self.portfolio_returns_history = deque(maxlen=self.reward_window_size) # Armazena retorenos do portifólio por passos
51
+
52
+ self.current_step = 0
53
+ self.balance = self.initial_balance
54
+ self.portfolio_weights = np.full(self.num_assets, 1.0 / self.num_assets if self.num_assets > 0 else 0) # Pesos iniciais iguais
55
+ self.portfolio_value = self.initial_balance
56
+ self.total_steps = len(self.df) - self.window_size -2 # -1 para ter um next_prices
57
+
58
+ # Espaço de Ação: pesos do portfólio para cada ativo (devem somar 1, via Softmax da rede)
59
+ # A rede neural vai outputar pesos que somam 1 (softmax).
60
+ self.action_space = spaces.Box(low=0, high=1, shape=(NUM_ASSETS,), dtype=np.float32)
61
+
62
+ # Espaço de Observação: janela de N features para M ativos (achatado)
63
+ # O número de colunas no df é num_assets * num_features_per_asset
64
+ num_total_features = self.df.shape[1]
65
+ self.observation_space = spaces.Box(
66
+ low=-np.inf, high=np.inf,
67
+ shape=(WINDOW_SIZE, NUM_ASSETS * NUM_FEATURES_PER_ASSET),
68
+ dtype=np.float32
69
+ )
70
+
71
+
72
+
73
+
74
+ self.current_prices_cols = [f"{key}_close" for key in self.asset_keys] # Assumindo que 'close' é uma das features base
75
+ # Se você usa 'close_div_atr', então seria f"{key}_close_div_atr"
76
+ # É importante ter uma coluna de preço de fechamento *original* (não escalada, não normalizada por ATR)
77
+ # para calcular os retornos reais do portfólio. Se não estiver no df, precisará ser adicionada/mantida.
78
+ # Por agora, vamos assumir que o df passado já tem as colunas de preço de fechamento originais,
79
+ # ou você precisará de um df separado só com os preços para o cálculo de retorno.
80
+ # VOU ASSUMIR QUE VOCÊ ADICIONA COLUNAS DE PREÇO DE FECHAMENTO ORIGINAIS AO `df_multi_asset_features`
81
+ # com nomes como `eth_orig_close`, `ada_orig_close` etc.
82
+ self.orig_close_price_cols = [f"{asset_prefix}_close" for asset_prefix in self.asset_keys] # Ex: 'crypto_eth_close'
83
+
84
+ # Verificar se as colunas de preço de fechamento original existem
85
+ missing_price_cols = [col for col in self.orig_close_price_cols if col not in self.df.columns]
86
+ if missing_price_cols:
87
+ raise ValueError(f"Colunas de preço de fechamento original ausentes no DataFrame do ambiente: {missing_price_cols}. "
88
+ "Adicione-as ao DataFrame com prefixo do ativo (ex: 'crypto_eth_close').")
89
+
90
+
91
+ def _get_observation(self):
92
+ # Pega as features da janela atual
93
+ # O DataFrame self.df já deve estar achatado e conter TODAS as features de TODOS os ativos
94
+ start = self.current_step
95
+ end = start + self.window_size
96
+ obs = self.df.iloc[start:end].values
97
+ return obs.astype(np.float32)
98
+
99
+ def _get_current_prices(self):
100
+ # Pega os preços de fechamento originais do passo atual para cálculo de retorno
101
+ # O índice é window_size - 1 dentro da observação atual, que corresponde a self.current_step + self.window_size -1 no df original.
102
+ # Mas para o cálculo de PnL, precisamos do preço no início do step e no final do step.
103
+ # Preço no início do step (t)
104
+ return self.df[self.orig_close_price_cols].iloc[self.current_step + self.window_size -1].values
105
+
106
+ def _get_next_prices(self):
107
+ # Preço no final do step (t+1)
108
+ return self.df[self.orig_close_price_cols].iloc[self.current_step + self.window_size].values
109
+
110
+ def reset(self, seed=None, options=None): # Assinatura atualizada do Gymnasium
111
+ super().reset(seed=seed) # Importante para Gymnasium
112
+ self.current_step = 0 # Inicia do primeiro ponto onde uma janela completa pode ser formada
113
+ self.balance = self.initial_balance
114
+ self.portfolio_value = self.initial_balance
115
+ self.portfolio_weights = np.full(self.num_assets, 1.0 / self.num_assets if self.num_assets > 0 else 0)
116
+ self.portfolio_returns_history.clear()
117
+
118
+ observation = self._get_observation()
119
+ info = self._get_info() # Informações adicionais (opcional)
120
+ return observation, info
121
+
122
+ def _calculate_sharpe_ratio(self) -> float:
123
+ """Calcula o Sharpe Ratio anualizado a partir do histórico de retornos por passo."""
124
+ if len(self.portfolio_returns_history) < self.reward_window_size / 2: # Precisa de um mínimo de dados
125
+ return 0.0 # Ou uma pequena penalidade por não ter histórico suficiente
126
+
127
+ returns_array = np.array(self.portfolio_returns_history)
128
+
129
+ # Média dos retornos por passo
130
+ mean_return_per_step = np.mean(returns_array)
131
+ # Desvio padrão dos retornos por passo
132
+ std_return_per_step = np.std(returns_array)
133
+
134
+ print(f" DEBUG Sharpe: mean_ret_step={mean_return_per_step:.6f}, std_ret_step={std_return_per_step:.6f}, risk_free_step={self.risk_free_rate_per_step:.8f}")
135
+
136
+ if std_return_per_step < 1e-9: # Evitar divisão por zero se não houver volatilidade
137
+ print(" DEBUG Sharpe: Std dev muito baixo, retornando 0.")
138
+ return 0.0
139
+
140
+ # Sharpe Ratio por passo
141
+ sharpe_per_step = (mean_return_per_step - self.risk_free_rate_per_step) / std_return_per_step
142
+
143
+ # Anualizar o Sharpe Ratio (assumindo passos horários e ~252 dias de negociação * 24 horas)
144
+ annualization_factor = np.sqrt(252 * 24) # Ajuste se seu timeframe for diferente
145
+
146
+ annualized_sharpe = sharpe_per_step * annualization_factor
147
+ print(f" DEBUG Sharpe: sharpe_per_step={sharpe_per_step:.4f}, annualized_sharpe={annualized_sharpe:.4f}")
148
+ return annualized_sharpe
149
+
150
+
151
+ def step(self, action_weights: np.ndarray): # Ação são os pesos do portfólio
152
+ current_portfolio_value_before_rebalance = self.portfolio_value
153
+
154
+ # Normalizar pesos da ação se não somarem 1 (saída softmax da rede já deve fazer isso)
155
+
156
+ if not np.isclose(np.sum(action_weights), 1.0):
157
+ action_weights = action_weights / (np.sum(action_weights) + 1e-9)
158
+ action_weights = np.clip(action_weights, 0, 1) # Garantir que os pesos estão entre 0 e 1 # Normaliza
159
+
160
+ # Calcular custo de transação para rebalancear
161
+ # Valor de cada ativo ANTES do rebalanceamento
162
+ current_asset_values = self.portfolio_weights * current_portfolio_value_before_rebalance
163
+ # Valor de cada ativo DEPOIS do rebalanceamento (com base nos novos pesos)
164
+ target_asset_values = action_weights * current_portfolio_value_before_rebalance # Valor do portfólio ainda não mudou por preço
165
+
166
+ # Volume negociado (absoluto) para cada ativo
167
+ trade_volume_per_asset = np.abs(target_asset_values - current_asset_values)
168
+ total_trade_volume = np.sum(trade_volume_per_asset)
169
+ transaction_costs = total_trade_volume * self.transaction_cost_pct
170
+
171
+ # Deduzir custos do valor do portfólio
172
+ current_portfolio_value_after_costs = current_portfolio_value_before_rebalance - transaction_costs
173
+
174
+ # Atualizar os pesos do portfólio
175
+ self.portfolio_weights = action_weights
176
+
177
+ # Pegar preços atuais (t) e próximos (t+1)
178
+ prices_t = self._get_current_prices()
179
+ self.current_step += 1 # Avançar para o próximo estado
180
+ prices_t_plus_1 = self._get_next_prices()
181
+
182
+ # Calcular retornos dos ativos
183
+ asset_returns_on_step = (prices_t_plus_1 - prices_t) / (prices_t + 1e-9)
184
+
185
+ # Calcular retorno do portfólio neste passo, APÓS custos e com os NOVOS pesos
186
+ portfolio_return_on_step = np.sum(self.portfolio_weights * asset_returns_on_step)
187
+
188
+ # Atualizar valor do portfólio
189
+ self.portfolio_value = current_portfolio_value_after_costs * (1 + portfolio_return_on_step)
190
+
191
+ # Adicionar retorno do passo ao histórico
192
+ self.portfolio_returns_history.append(portfolio_return_on_step)
193
+
194
+ # Calcular Recompensa (Sharpe Ratio)
195
+ # Pode ser o Sharpe Ratio incremental ou o Sharpe Ratio da janela inteira
196
+ # Para RL, uma recompensa mais frequente é geralmente melhor.
197
+ # Usar o retorno do passo como recompensa imediata pode ser mais estável para PPO.
198
+ # Ou, podemos dar o Sharpe Ratio da janela como recompensa a cada N passos, ou no final.
199
+ # Por agora, vamos usar o retorno do passo como recompensa principal, e o Sharpe pode ser parte do 'info'.
200
+ # Se quisermos o Sharpe Ratio *como* recompensa, ele seria calculado aqui.
201
+
202
+ # Opção A: Recompensa = Retorno do Passo (mais simples e denso)
203
+ # ultima iteração -- reward = portfolio_return_on_step
204
+
205
+ #Opção B: Recompensa = Sharpe Ratio da Janela (mais complexo, pode ser esparso se calculado raramente)
206
+ # Em PortfolioEnv.step()
207
+ # reward = portfolio_return_on_step # Recompensa atual
208
+
209
+ # NOVA RECOMPENSA (Opção B da nossa discussão anterior):
210
+ REWARD_SCALE_FACTOR_SHARPE = 0.1 # Ou 0.01, experimente
211
+ if len(self.portfolio_returns_history) >= self.reward_window_size:
212
+ current_sharpe = self._calculate_sharpe_ratio()
213
+ reward = np.clip(current_sharpe, -5, 5) * REWARD_SCALE_FACTOR_SHARPE
214
+ print(f" Sharpe Ratio Calculado: {current_sharpe:.4f}, Recompensa (Sharpe escalado): {reward:.6f}")
215
+ elif len(self.portfolio_returns_history) > 1:
216
+ reward = portfolio_return_on_step * 0.1
217
+ print(f" Retorno Simples como Recompensa (escalado): {reward:.6f}")
218
+ else:
219
+ reward = 0.0
220
+
221
+ terminated = self.current_step >= self.total_steps
222
+ truncated = False
223
+
224
+ observation = self._get_observation()
225
+ info = self._get_info() # Adicionar Sharpe Ratio ao info
226
+
227
+ return observation, reward, terminated, truncated, info
228
+
229
+
230
+ def _get_info(self): # Opcional, para retornar métricas
231
+ current_sharpe = self._calculate_sharpe_ratio() if len(self.portfolio_returns_history) > 1 else 0.0
232
+ return {
233
+ "current_step": self.current_step,
234
+ "portfolio_value": self.portfolio_value,
235
+ "balance": self.balance, # Se você rastrear cash separadamente
236
+ "portfolio_weights": self.portfolio_weights.tolist(),
237
+ "last_step_return": self.portfolio_returns_history[-1] if self.portfolio_returns_history else 0.0,
238
+ "sharpe_ratio_window": current_sharpe
239
+ }
240
+
241
+ def render(self, mode='human'):
242
+ if mode == 'human':
243
+ print(f"Step: {self.current_step}, Portfolio Value: {self.portfolio_value:.2f}, Weights: {self.portfolio_weights}")
244
+
245
+ def close(self):
246
+ pass # Limpar recursos se necessário
agents/agents/portfolio_features_extractor_torch.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+ from stable_baselines3.common.torch_layers import BaseFeaturesExtractor
4
+ from deep_portfolio_torch import DeepPortfolioAgentNetworkTorch
5
+
6
+ class PortfolioFeaturesExtractorTorch(BaseFeaturesExtractor):
7
+ def __init__(self, observation_space, features_dim=32,
8
+ num_assets=4, sequence_length=60, num_features_per_asset=26,
9
+ asset_cnn_filters1=32, asset_cnn_filters2=64,
10
+ asset_lstm_units1=64, asset_lstm_units2=32,
11
+ final_dense_units1=128, final_dense_units2=32,
12
+ final_dropout=0.3, mha_num_heads=4, mha_key_dim_divisor=2,
13
+ output_latent_features=True, use_sentiment_analysis=False):
14
+ super().__init__(observation_space, features_dim)
15
+ self.network = DeepPortfolioAgentNetworkTorch(
16
+ num_assets=num_assets,
17
+ sequence_length=sequence_length,
18
+ num_features_per_asset=num_features_per_asset,
19
+ asset_cnn_filters1=asset_cnn_filters1,
20
+ asset_cnn_filters2=asset_cnn_filters2,
21
+ asset_lstm_units1=asset_lstm_units1,
22
+ asset_lstm_units2=asset_lstm_units2,
23
+ final_dense_units1=final_dense_units1,
24
+ final_dense_units2=final_dense_units2,
25
+ final_dropout=final_dropout,
26
+ mha_num_heads=mha_num_heads,
27
+ mha_key_dim_divisor=mha_key_dim_divisor,
28
+ output_latent_features=output_latent_features,
29
+ use_sentiment_analysis=use_sentiment_analysis
30
+ )
31
+ self._features_dim = features_dim
32
+
33
+ def forward(self, observations):
34
+ # observations: (batch, seq_len, num_assets * num_features_per_asset)
35
+ return self.network(observations)
agents/agents/ppo_deep_portfolio_tensorboard/PPO_1/events.out.tfevents.1750287361.verticalagent-X555LPB.89910.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:416a6f624b3dae44c64d6ad216c0d9c90927c65aa2fffc56dde39d387a66b0d2
3
+ size 83375
agents/agents/ppo_deep_portfolio_tensorboard/PPO_10/events.out.tfevents.1750497081.codespaces-72cb68.2589.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6ea8284249717efcc6b46e7957eef6b8ceff0c6d92487095fdbe12f141125074
3
+ size 254591
agents/agents/ppo_deep_portfolio_tensorboard/PPO_11/events.out.tfevents.1750534135.codespaces-72cb68.3018.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8c4af51ef094dc62cefc525b73b30f2f35ea2a3572dc386c6653c97511d71d8f
3
+ size 255329
agents/agents/ppo_deep_portfolio_tensorboard/PPO_12/events.out.tfevents.1750560310.codespaces-72cb68.253920.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:37b09133c93eec65e524f5a76873048d399c6dab52f5516f1a01e4c6df736f2a
3
+ size 14741
agents/agents/ppo_deep_portfolio_tensorboard/PPO_13/events.out.tfevents.1750568153.codespaces-72cb68.2534.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:790362af681bc65a145098768cb0d33b0537524b368420f92f1902de8c559e1b
3
+ size 177101
agents/agents/ppo_deep_portfolio_tensorboard/PPO_14/events.out.tfevents.1750587177.verticalagent-X555LPB.125274.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5a125c886db6fb22c394b8fc76d1ac2c99ed0fe68ba5713a69e4fea0551c5c35
3
+ size 36143
agents/agents/ppo_deep_portfolio_tensorboard/PPO_15/events.out.tfevents.1750636729.verticalagent-X555LPB.266088.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ed84c8ebfc3274f6f2d0dff3f5f7278f4aca81954e7f45121a0180504654e164
3
+ size 135
agents/agents/ppo_deep_portfolio_tensorboard/PPO_16/events.out.tfevents.1750638335.verticalagent-X555LPB.270772.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:68d42ca276a5b5bc587bd082ac51200daca54a62ba8a75af0bcd916712f4ebf4
3
+ size 88
agents/agents/ppo_deep_portfolio_tensorboard/PPO_17/events.out.tfevents.1750638480.verticalagent-X555LPB.271132.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e42d9077440ade5a761b7142c9645030018a476581f6e1701f809afe490c58ac
3
+ size 135
agents/agents/ppo_deep_portfolio_tensorboard/PPO_18/events.out.tfevents.1750639418.verticalagent-X555LPB.273960.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:811fff7b41423665bb07a8490f6d6c8eb4cba229a7f40de47eb2e51fba4e35a3
3
+ size 1353